Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 14722|回复: 0

react hooks 全面转换攻略(三) 全局存储解决方案

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-4-12 18:00:37 | 显示全部楼层 |阅读模式

    针对 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
        }
      }
    })
    // 导出 useModal
    export 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 类,可以用现成的,也可以使用自己写的方案

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-4 11:51 , Processed in 0.073321 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表