针对 react hooks 的新版本解决方案

一.redux维持原方案

若想要无缝使用原来的 redux,和其配套的中间件 promise,thunk,saga 等等的话
可以使用 redux-react-hook

github 链接 redux-react-hook

一个简单的使用例子:

import {useDispatch, useMappedState} from 'redux-react-hook';export function DeleteButton({index}) {  // 类似于以前 react-redux 中的 connect 函数  const mapState = useCallback(    state => ({      canDelete: state.todos[index].canDelete,      name: state.todos[index].name,    }),    [index],  );  // 获取 redux 的数据  const {canDelete, name} = useMappedState(mapState);  // 获取 dispatch   const dispatch = useDispatch();      // button click handle  const deleteTodo = useCallback(    () =>      dispatch({        type: 'delete todo',        index,      }),    [index],  );  return (    <button disabled={!canDelete} onClick={deleteTodo}>      Delete {name}    </button>  );}

使用方法和以前一致

二.使用 useReducer 与 context

在 index 或 app 中提供全局的 redux 与 dispatch

function isPromise(obj) {  return (    !!obj &&    (typeof obj === "object" || typeof obj === "function") &&    typeof obj.then === "function"  );}function wrapperDispatch(dispatch) {  // 功能和 redux-promise 相同  return function (action) {    isPromise(action.payload) ?      action.payload.then(v => {        dispatch({type: action.type, payload: v})      }).catch((error) => {        dispatch(Object.assign({}, action, {          payload: error,          error: true        }));        return Promise.reject(error);      })      :      dispatch(action);  };}function Wrap(props) {  // 确保在 dispatch 后不会刷新APP组件  const [state, dispatch] = useReducer(reducers, ReducersValue);  console.log('render wrap')  return (<MainContext.Provider value={{state: state, dispatch: wrapperDispatch(dispatch)}}>{props.children}</MainContext.Provider>)}function App() {  console.log('render  App')  return <Wrap>    <Router>      <Switch>        <Route path="/login" component={Login} exact/>        <Route path="/" component={MainIndex}/>      </Switch>    </Router>  </Wrap>}

具体使用:

function useDispatch() {  // 获取 dispatch  const store = useContext(MainContext);  return store.dispatch;}function useStoreState(mapState) {  //存储 state 且判断是否需要 render  const {state:store} = useContext(MainContext);  const mapStateFn = () => mapState(store);  const [mappedState, setMappedState] = useState(() => mapStateFn());  const lastRenderedMappedState = useRef();  // Set the last mapped state after rendering.  useEffect(() => {    lastRenderedMappedState.current = mappedState;  });  useEffect(    () => {     console.log('useEffect ')      const checkForUpdates = () => {        const newMappedState = mapStateFn();        if (!_.isEqual(newMappedState, lastRenderedMappedState.current)) {          setMappedState(newMappedState);        }      };      checkForUpdates();    },    [store, mapState],  );  return mappedState}// 组件内使用const ResourceReducer = useStoreState(state => state.ResourceReducer)const dispatch = useDispatch()

他的功能已经足够了,在使用的地方使用函数即可,很方便
但是也有一些不足的地方是在根源上的,即 context,
在同一个页面中 如果有多个使用 context 的地方
那么如果一旦dispatch ,其他的所有地方也会触发render 造成资源的浪费,小项目还好,大项目仍旧不可

(除非 react 的 context 函数添加 deps)

三.自定义解决方案

原理就是存储一个全局变量 ,通过 import 引入;
我自己写了一个例子:https://github.com/Grewer/react-hooks-store
想要基础的实现只需要 30+ 行的代码即可

class Modal {  private value: any;  private prevValue: any;  private reducers: (state, action) => {};  private queue: any = [];  private dispatch: (action) => void;  constructor(reducers) {    this.reducers = combineReducers(reducers)    // combineReducers 来自于 reudx ,可以引入也可以自己写一个(后续我会写一个库,会包含此函数)    this.value = this.reducers({}, {})    this.dispatch = action => {      this.prevValue = this.value;      this.value = this.reducers(this.value, action)      this.onDataChange()    }  }  useModal = (deps?: string[]) => {    const [, setState] = useState(this.value);    useEffect(() => {      const index = this.queue.push({setState, deps}); // 订阅      return () => { // 组件销毁时取消        this.queue.splice(index - 1, 1);      };    }, []);    return [this.value, this.dispatch]  }  onDataChange = () => {    this.queue.forEach((queue) => {      const isRender = queue.deps ? queue.deps.some(dep => this.prevValue[dep] !== this.value[dep]) : true      isRender && queue.setState(this.value)    });  }}

// 初始化 reducers

const modal = new Modal({  countReducer: function (state = 0, action) {    console.log('count Reducer', state, action)    switch (action.type) {      case "ADD":        console.log('trigger')        return state + action.payload || 1      default:        return state    }  },  listReducer: function (state = [] as any, action) {    console.log('list Reducer', state, action)    switch (action.type) {      case "ADD_LIST":        console.log('trigger')        state.push(action.payload)        return [...state]      default:        return state    }  },  personReducer: function (state = {name: 'lll', age: 18} as any, action) {    console.log('person Reducer', state, action)    switch (action.type) {      case "CHANGE_NAME":        return Object.assign({}, state, {name: action.payload})      default:        return state    }  }})// 导出 useModalexport const useModal = modal.useModal

简单的使用:

function Count(props) {  const [state, dispatch] = useModal(['countReducer'])  // 非 countReducer 的更新 不会触发此函数 render  console.warn('render Count', state, dispatch)  return <div>    <button onClick={() => dispatch({type: "ADD", payload: 2})}>+</button>  </div>}

当然你也可以自己写一个,自己想要的方案

总结

hooks 的存储方案基本就这 3 类,可以用现成的,也可以使用自己写的方案

相关文章:

  • react hooks 全面转换攻略(一) react本篇之useState,useEffect
  • react hooks 全面转换攻略(二) react本篇剩余 api
  • react hooks 全面转换攻略(三) 全局存储解决方案