V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
steins2628
V2EX  ›  React

React useContext 值改变不引发重绘

  •  
  •   steins2628 · 2022-06-14 14:02:50 +08:00 · 3800 次点击
    这是一个创建于 928 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如题,想要动态编辑列表,用 dnd-kit 作了拖拽,拖拽功能都正常

    触发编辑的按钮放在了兄弟组件里,打算用 useContext 共享一些数据

    现在的问题是 useContext 能够成功触发改变数据,但是并不会引起组件重绘,导致无法正常控制组件的修改

    好奇这是为什么?有什么解决方法?或者是不是有什么更好的做法?

    我看还有的博客里直接把 setState 放到了 useContext 里,结果我用的时候直接报 setxxx not a function ,这是不是取巧失败了?参考的是这篇博客 复杂情况下的组件通信 Hooks:useContext

    求大佬赐教 CodeSandbox 示例代码

    第 1 条附言  ·  2022-06-17 14:11:41 +08:00
    终于解决了,半路还被 useState 异步同步坑了,不过现在是能用了
    useReducer 和 useState 方法都实现了一遍,代码还是在 codeSandbox 里
    希望对有需要的人有点参考价值
    18 条回复    2022-06-16 10:10:41 +08:00
    alexsunxl
        1
    alexsunxl  
       2022-06-14 14:12:57 +08:00
    简单点,用个 recoil 之类的状态管理库?
    TWorldIsNButThis
        2
    TWorldIsNButThis  
       2022-06-14 14:19:32 +08:00 via iPhone   ❤️ 1
    react 触发 rerender 只有一个来源就是 setstate
    你直接赋值肯定触发不了啊
    把 set 也传过去,onclick 的地方用 set 更新值
    另外这里有三个都要传可以考虑用 reducer
    steins2628
        3
    steins2628  
    OP
       2022-06-14 14:25:44 +08:00
    @alexsunxl 项目本体是用 antd pro 搭的,状态管理倒是有
    但我这点数据也就组件内部消化,感觉还是 useContext 更适合,就是没用好
    hanai
        4
    hanai  
       2022-06-14 14:33:28 +08:00 via iPhone
    用法不对,对 context 里的值要用 setState 修改,建议看 react 关于 context 的官方文档。
    westoy
        5
    westoy  
       2022-06-14 14:44:40 +08:00
    MyDragList 里用 useState 生成 items 和 setItems , 再分别传给 MyDrag 和 MyControl 啊, 不要搞的那么复杂......
    darkengine
        6
    darkengine  
       2022-06-14 14:54:07 +08:00
    看了 codeSandbox 里的效果,其实不需要用到 useContext 的,因为数据只在两级组件里流转
    alexsunxl
        7
    alexsunxl  
       2022-06-14 15:10:05 +08:00
    @darkengine 你想说传 props 吗 哈哈哈,一直都不喜欢传 props 。稍微改动调整一下结构就有点难受。
    darkengine
        8
    darkengine  
       2022-06-14 15:12:04 +08:00
    @alexsunxl 嗯,就是 props 。我项目里用到 useContext 的场景只有两个,一个是反映当前用户的 account context ,一个是切换 UI 主题的 theme context 。
    FaiChou
        9
    FaiChou  
       2022-06-14 15:58:51 +08:00
    ContextAPI 在历史上的确有过深层次子组建不能刷新的问题, 但已经解决了. 也就是说 Context 解决了深层子组件 re-render 链被打断的这个问题, 写了一个简单的 demo 来验证下:

    要注意下 Component1 是被 React.memo(), 其次 Provider 的 value 也需要用 useMemo() 来固定, 否则每次点击 update 都会变成新的 setVal 值.

    const Context = createContext()
    const Component1 = React.memo(function() {
    console.log('componet1')
    return (
    <div>
    <h1>Component1</h1>
    <Component2 />
    </div>
    )
    })
    function Component2() {
    console.log('componet2')
    const { val } = useContext(Context);
    return (
    <div>
    <h1>Component2</h1>
    <h2>{val}</h2>
    </div>
    )
    }
    function App() {
    const [, forceUpdate] = useReducer(x=>x+1,0);
    const [val, setVal] = useState(null);
    return (
    <Context.Provider value={useMemo(()=>({ val, setVal }), [val])}>
    <Component1 />
    <button onClick={forceUpdate}>update</button>
    <button onClick={() => setVal(Date.now().toString())}>setRandom</button>
    </Context.Provider>
    );
    }
    FaiChou
        10
    FaiChou  
       2022-06-14 16:05:02 +08:00
    gogogo1203
        11
    gogogo1203  
       2022-06-14 16:42:16 +08:00
    可以快进到 10:30 左右 你需要的是 selector hook. 市面上有现成的 useSelector
    gogogo1203
        12
    gogogo1203  
       2022-06-14 16:43:19 +08:00
    很多时候 React 的所谓 re-render, 并不是 Dom 的 re-render. 很多情况下是没有什么大不了的。
    joesonw
        13
    joesonw  
       2022-06-14 16:55:41 +08:00 via iPhone
    要像 useContext 一样用的爽,可以试试 recoil 。
    gogogo1203
        14
    gogogo1203  
       2022-06-14 16:58:17 +08:00
    我最近做了一个 draggable 的云顶攻略,https://homebh.tk/editor zustand+immer+reducer

    const [
    tag,
    clearStore,
    ] = useEditorStore(
    (state) => [
    state.tag,
    state.clearStore,
    ],
    shallow
    )

    shallow 可以进行对比, 从而避免一些 re-render. useSelector 基本都是这个原理,做一个浅比对
    Exuanbo
        15
    Exuanbo  
       2022-06-14 18:07:25 +08:00
    Context 只相当于一个依赖注入 API 而不是状态管理工具,你需要把 setState 传进去
    KAROTT
        16
    KAROTT  
       2022-06-14 23:28:16 +08:00
    Context 本质还是一个组件,只不过可以跨组件传递和接收属性;要更新数据统一使用 useState 执行后的 set 方法
    demonzoo
        17
    demonzoo  
       2022-06-14 23:34:13 +08:00
    你不 setState 怎么触发 re-render 呢。。。
    steins2628
        18
    steins2628  
    OP
       2022-06-16 10:10:41 +08:00
    @TWorldIsNButThis
    试着改了 useReducer 的版本,但在拖拽更改列表顺序的时候,state.listItems 无法 map, 打印出来是数组应该是可以 map 的,这是 state 下 的特殊情况,还是其实我用的方法不对?

    @hanai
    @Exuanbo
    @KAROTT
    @demonzoo
    试着改了 useState 的版本,但在父组件的 useEffect 里赋初值好像也不会触发子组件重绘
    看着像是 setState 失败了,是不是我传 set 的方法有问题?
    https://codesandbox.io/s/dnd-kit-with-custom-item-8oc96b?file=/src/useContext_useState/index.tsx


    @westoy
    @darkengine
    直接 props 是成功的,但还是想试试 useContext ,感觉会简洁很多,以后也好改


    @FaiChou 学习了


    @gogogo1203 原来还有这种 hook 学习了


    @joesonw 暂时想用原生的试试,因为这个不需要全局状态


    @gogogo1203 大佬做的真好
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1056 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 19:01 · PVG 03:01 · LAX 11:01 · JFK 14:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.