React useContext 值改变不引发重绘

2022-06-14 14:02:50 +08:00
 steins2628

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

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

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

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

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

求大佬赐教 CodeSandbox 示例代码

3394 次点击
所在节点    React
18 条回复
alexsunxl
2022-06-14 14:12:57 +08:00
简单点,用个 recoil 之类的状态管理库?
TWorldIsNButThis
2022-06-14 14:19:32 +08:00
react 触发 rerender 只有一个来源就是 setstate
你直接赋值肯定触发不了啊
把 set 也传过去,onclick 的地方用 set 更新值
另外这里有三个都要传可以考虑用 reducer
steins2628
2022-06-14 14:25:44 +08:00
@alexsunxl 项目本体是用 antd pro 搭的,状态管理倒是有
但我这点数据也就组件内部消化,感觉还是 useContext 更适合,就是没用好
hanai
2022-06-14 14:33:28 +08:00
用法不对,对 context 里的值要用 setState 修改,建议看 react 关于 context 的官方文档。
westoy
2022-06-14 14:44:40 +08:00
MyDragList 里用 useState 生成 items 和 setItems , 再分别传给 MyDrag 和 MyControl 啊, 不要搞的那么复杂......
darkengine
2022-06-14 14:54:07 +08:00
看了 codeSandbox 里的效果,其实不需要用到 useContext 的,因为数据只在两级组件里流转
alexsunxl
2022-06-14 15:10:05 +08:00
@darkengine 你想说传 props 吗 哈哈哈,一直都不喜欢传 props 。稍微改动调整一下结构就有点难受。
darkengine
2022-06-14 15:12:04 +08:00
@alexsunxl 嗯,就是 props 。我项目里用到 useContext 的场景只有两个,一个是反映当前用户的 account context ,一个是切换 UI 主题的 theme context 。
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
2022-06-14 16:05:02 +08:00
gogogo1203
2022-06-14 16:42:16 +08:00
<amp-youtube data-videoid="aOt4Hz3ze3Q" layout="responsive" width="480" height="270"></amp-youtube> 可以快进到 10:30 左右 你需要的是 selector hook. 市面上有现成的 useSelector
gogogo1203
2022-06-14 16:43:19 +08:00
很多时候 React 的所谓 re-render, 并不是 Dom 的 re-render. 很多情况下是没有什么大不了的。
joesonw
2022-06-14 16:55:41 +08:00
要像 useContext 一样用的爽,可以试试 recoil 。
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
2022-06-14 18:07:25 +08:00
Context 只相当于一个依赖注入 API 而不是状态管理工具,你需要把 setState 传进去
KAROTT
2022-06-14 23:28:16 +08:00
Context 本质还是一个组件,只不过可以跨组件传递和接收属性;要更新数据统一使用 useState 执行后的 set 方法
demonzoo
2022-06-14 23:34:13 +08:00
你不 setState 怎么触发 re-render 呢。。。
steins2628
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 大佬做的真好

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/859532

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX