一个使用React钩子和Context实现的高度可测试TodoList应用程序

网友投稿 882 2022-10-30

一个使用React钩子和Context实现的高度可测试TodoList应用程序

一个使用React钩子和Context实现的高度可测试TodoList应用程序

React Hooks Todo App

A trial to achieve a correct approach. Trying to get rid off using Redux, make contexts more useful with useReducer and make components "easy-to-test simple functions".

A highly decoupled, testable TodoList app that uses React hooks.

This is a training repo to learn about new hooks feature of React and creating a testable environment.

Zero-dependencyNo class componentsUses Context to share a global stateUses useReducer to manage state actionsuseState to create local stateDecoupled state logic (Actions)Testable components (Uses Jest + Enzyme for tests)Custom Hooks for persisting state.

For better approaches please open Pull Requests

Summary

1. Context:

The main approach was to get rid off Redux and use React Contexts instead. With the composition of useState, useContext I created a global state. And passed it into a custom hook called useTodos. useTodos curries useState output and generates a state manager which will be passed into TodoContext.Provider to be used as a global state.

function App() { // create a global store to store the state const globalStore = useContext(Store); // `todos` will be a state manager to manage state. const [state, dispatch] = useReducer(reducer, globalStore); return ( // State.Provider passes the state and dispatcher to the down );}

2. The Reducer:

The second approach was to seperate the main logic, just as the actions of Redux. But these are fully functional, every function returns whole state.

// Reducer is the classical reducer that we know from Redux.// used by `useReducer`export default function reducer(state, action) { switch (action.type) { case "ADD_TODO": return { ...state, todos: [...state.todos, action.payload] }; case "COMPLETE": return { ...state, todos: state.todos.filter(t => t !== action.payload) }; default: return state; }}

3. State and Dispatcher

I reach out state and dispathcer of context using useContext and I can reach to the actions.

import React, { useContext } from "react";import Store from "../context";export default function TodoForm() { const { state, dispatch } = useContext(Store); // use `state.todos` to get todos // use `dispatch({ type: 'ADD_TODO', payload: 'Buy milk' })`

4. Persistence with custom hooks:

I created custom hooks to persist state on localStorage

import { useEffect } from "react";// Accepts `useContext` as first parameter and returns cached context.export function usePersistedContext(context, key = "state") { const persistedContext = localStorage.getItem(key); return persistedContext ? JSON.parse(persistedContext) : context;}// Accepts `useReducer` as first parameter and returns cached reducer.export function usePersistedReducer([state, dispatch], key = "state") { useEffect(() => localStorage.setItem(key, JSON.stringify(state)), [state]); return [state, dispatch];}

The App function will be:

function App () { const globalStore = usePersistedContext(useContext(Store)); // `todos` will be a state manager to manage state. const [state, dispatch] = usePersistedReducer(useReducer(reducer, globalStore));

5. Everything is testable decoupled:

The last but most important part of the approach is to make all the parts testable. They don't tie to eachother which makes me to write tests easily.

License

MIT

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Pytorch实现CNN(15)
下一篇:Linux软件安装、卸载指令
相关文章

 发表评论

暂时没有评论,来抢沙发吧~