useState
始终触发更新,即使数据值未更改。
这是该问题的有效演示:demo
我正在使用useState
钩子来更新对象,并且试图使其仅在该对象中的值更改时才更新。因为React使用Object.is比较算法来确定何时更新。具有相等值的对象仍然是导致对象重新渲染的原因,因为它们是不同的对象。
例如即使有效载荷的值保持为{ foo: 'bar' }
const UseStateWithNewObject = () => {
const [payload, setPayload] = useState({});
useEffect(
() => {
setInterval(() => {
setPayload({ foo: 'bar' });
}, 500);
},
[setPayload]
);
renderCountNewObject += 1;
return <h3>A new object, even with the same values, will always cause a render: {renderCountNewObject}</h3>;
};
我是否可以使用钩子实现类似shouldComponentUpdate
的东西,以告诉他们仅在数据更改时才重新渲染组件?
答案 0 :(得分:2)
我认为我们需要看到一个更好的现实生活示例,说明您想做什么,但是从您共享的观点来看,我认为逻辑需要在状态被设定之前向上游移动。
例如,您可以在更新状态之前手动比较useEffect
中的输入值,因为这基本上就是您要问React是否可以为您做的事情。
在这种情况下,您可能会使用一个库use-deep-compare-effect
https://github.com/kentcdodds/use-deep-compare-effect,它会涉及很多手动工作,但是即使如此,该解决方案仍假定开发人员正在(根据传入的道具等)手动决定是否应更新状态。
例如:
const obj = {foo: 'bar'}
const [state, setState] = useState(obj)
useEffect(() => {
// manually deep compare here before updating state
if(obj.foo === state.foo) return
setState(obj)
},[obj])
编辑:如果您不直接使用该值并且不需要该组件根据其更新,则使用useRef
的示例:
const obj = {foo: 'bar'}
const [state, setState] = useState(obj)
const { current: payload } = useRef(obj)
useEffect(() => {
// always update the ref with the current value - won't affect renders
payload = obj
// Now manually deep compare here and only update the state if
//needed/you want a re render
if(obj.foo === state.foo) return
setState(obj)
},[obj])
答案 1 :(得分:1)
我是否可以使用钩子来实现诸如shouldComponentUpdate之类的内容,以告知仅在数据更改时才重新渲染组件?
常见的,对于状态更改,您在使用功能useState
或使用useRef
进行引用之前将其与先前的值进行比较:
// functional useState
useEffect(() => {
setInterval(() => {
const curr = { foo: 'bar' };
setPayload(prev => (isEqual(prev, curr) ? prev : curr));
}, 500);
}, [setPayload]);
// with ref
const prev = useRef();
useEffect(() => {
setInterval(() => {
const curr = { foo: 'bar' };
if (!isEqual(prev.current, curr)) {
setPayload(curr);
}
}, 500);
}, [setPayload]);
useEffect(() => {
prev.current = payload;
}, [payload]);
为完整起见,“在数据更改时重新渲染组件?”可能也称为道具,因此在这种情况下,您应该使用React.memo
。
如果给定相同的道具,功能组件呈现相同的结果,则可以将其包装在对React.memo的调用中,以在某些情况下通过记忆结果来提高性能。这意味着React将跳过渲染组件,并重用最后渲染的结果。
答案 2 :(得分:0)
对此通用解决方案不涉及在效果中添加逻辑,而是将组件拆分为:
obj.increment
记忆的您的笨拙组件可以是纯净的(就像实现了React.memo
一样,您的智能状态处理组件也可以是“笨拙”的,而不必担心将状态更新为相同的值。
示例:
shouldComponentUpdate
export default function Foo() {
const [state, setState] = useState({ foo: "1" })
const handler = useCallback(newValue => setState({ foo: newValue }))
return (
<div>
<SomeWidget onEvent={handler} />
Value: {{ state.foo }}
</div>
)
这使您可以分离所需的逻辑。
答案 3 :(得分:0)
如果您检查类型为object或对象数组的props的更改,我建议在useEffect deps上使用JSON.stringify。很简单。
const obj = {foo: 'bar'}
const [state, setState] = useState(obj)
useEffect(() => {
setState(obj)
},[JSON.stringify(obj)])
答案 4 :(得分:-1)
您可以使用已记忆的组件,它们只会在更改道具时重新呈现。
const comparatorFunc = (prev, next) => {
return prev.foo === next.foo
}
const MemoizedComponent = React.memo(({payload}) => {
return (<div>{JSON.stringify(payload)}</div>)
}, comparatorFunc);