请教 react-redux 中 connect 的实现 - V2EX
ethusdt
0.01D
V2EX    React

请教 react-redux 中 connect 的实现

  •  
  •   ethusdt Feb 19, 2022 2875 views
    This topic created in 1566 days ago, the information mentioned may be changed or developed.

    两年没写代码, 之前用过 React, 但没(经常)用 hooks.

    最近看了下文档, 遇到几个问题, 比如, 外部的数据(redux)变化如何让组件 rerender?

    如果是我去实现 connect, 我会写成一个 HOC, 添加一个 listener 监听 state 变化, 当发生变化, 手动调用 forceUpdate.

    但看了下现在 connect 源码, 没太看懂, 看到是用 Context 实现的, 但具体逻辑有几个问题:

    1 具体实现方式

    如果是 Context 实现, 那外层应该是一个 Context.Provider, connect 应该是对 Consumer 的封装, 但发现还是用 Provider 的封装. 一个项目成千上万的小组件使用了 connect, 那就会有成千上万个 Provider, 逻辑岂不很混乱?

    2 为什么不写成 connect(Component, mapStateToProps, mapDispatchtoProps)?

    现在的实现(pseudocode):

    function connect(mapStateToProps, mapDispatchtoProps...) { // some logic1 return function(Component) { // some logic2 return <Component {...props} /> } } 

    使用的时候需要 connect(mapState, mapProps)(Component).

    为什么不实现成这样(pseudocode):

    function connect(Component, mapStateToProps, mapDispatchtoProps...) { // some logic with mapState and mapDispatch return <Component {...props} /> } 
    11 replies    2022-02-24 16:35:58 +08:00
    ethusdt
        1
    ethusdt  
    OP
       Feb 19, 2022
    const compose = (...funcs) =>
    funcs.reduce((a, b) => (...args) => a(b(...args)), arg => arg)

    问题 2, 难道是为了 functional programming ?
    7anshuai
        2
    7anshuai  
       Feb 19, 2022   2
    https://react-redux.js.org/api/connect#connect-returns

    connect() 返回一个 HOC ,HOC 可以重复使用
    nondanee
        3
    nondanee  
       Feb 19, 2022   2
    可能是为了方便用装饰器?

    ```
    @connect(mapStateToProps, mapDispatchtoProps...)
    class MyComponent extends PureComponent {
    render () {}
    }
    ```
    maplelin
        4
    maplelin  
       Feb 19, 2022
    @nondanee #3 这个肯定不是,只是 HOC 的用法就是这样,connect 其实可以理解成返回一个函数式的 HOC ,说白了里面闭包的部分就是一个组件了,而不是单纯的 function ,可以用 connect 返回的那个 function 包装出多个不同 UI 的组件,比单纯的 return component 复用性高不少
    otakustay
        5
    otakustay  
       Feb 19, 2022
    理论上来说,connect(mapState, mapDispatch)的结果应该是一个可复用的东西,比如:

    const cOnnectCurrentUser= connect(state => state.session.currentUser);
    const cOnnectItemList= connect(
    state => state.items.all,
    dispatch => ({reload: () => dispatch('RELOAD_ITEMS')})
    );

    然后需要用当前用户的地方,就 connectCurrentUser(MyComponent)就行

    但实践中,不知怎么着,就几乎没人把 connect 返回的那个函数复用,最终就有了楼主这样不理解 connect 设计的人了
    otakustay
        6
    otakustay  
       Feb 19, 2022
    关于实现,beta 版现在应该是用 useSyncExternalStore 实现 useSelector 和 useDispatch 了,大概十来行就够用了。然后 connect 是可以基于 useSelector 和 useDispatch 二次封装的:

    const cOnnect= (mapState, mapDispatch) => {
    return CompOnentIn=> props => {
    const state = useSelector(mapState);
    const dispatch = useDispatch();
    const methods = mapDispatch(dispatch);
    return <ComponentIn {...props} {...state} {...methods} />;
    };
    }
    ethusdt
        7
    ethusdt  
    OP
       Feb 19, 2022
    @otakustay 这种简单实现我也懂, 我是不明白源码中为何要多套一层 <ContextToUse.Provider>:

    Provider:
    const COntext= React.createContext(null);
    function Provider(store, children) {
    return <Context.Provider value={contextValue}>{childeren}</Context.Provider>
    }


    const cOnnect= (mapState, mapDispatch) => {
    return CompOnentIn=> props => {
    const state = useSelector(mapState);
    const dispatch = useDispatch();
    const methods = mapDispatch(dispatch);
    return (
    <ContextToUse.Provider value={overriddenContextValue}>
    <ComponentIn {...props} {...state} {...methods} />;
    </ContextToUse.Provider>
    );
    };
    }
    otakustay
        8
    otakustay  
       Feb 19, 2022
    @FaiChou #7 没有 Provider 就拿不到 store 啊,store 是用户创建的,redux 不知道在哪里。就算我的那个简单实现,useSelector 和 useDispatch 的实现里面也是通过 useContext 拿到 store ,再用 useSyncExternalStore 监听 store 的
    ethusdt
        9
    ethusdt  
    OP
       Feb 20, 2022
    @otakustay 你还是没有看源码,「没有 Provider 就拿不到 store 」是的, 所以 react-redux 在最外层的 Provider 使用了 <Context.Provider> :

    https://github.com/reduxjs/react-redux/blob/master/src/components/Provider.tsx

    但是在 connect 中, 它 wrap 的组件外再套了一层 <ContextToUse.Provider> , 难道不是应该套一层 <Consumer> 吗?
    ethusdt
        10
    ethusdt  
    OP
       Feb 20, 2022
    @otakustay 我明白了. 原来并不是 store 改变 <Provider>下面所有的 tree 都需要 update:

    > Because each connected component should only re-render when its nearest connected ancestor component has rendered.

    https://github.com/reduxjs/react-redux/discussions/1875#discussioncomment-2210998
    About     Help     Advertise     Blog     API     FAQ     Solana     5376 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 67ms UTC 07:04 PVG 15:04 LAX 00:04 JFK 03:04
    Do have faith in what you're doing.
    /div>
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86