重新渲染键值对对象组件

Re-rendering on key-value pair object components

我想避免在 <ChildComponent/> 中使用 onClick 更新我的状态时重新渲染我的子组件 <ChildComponent/>。 我在 <ParentComponent/> 中有我的回调函数,它更新键值对对象的值之一。

在父组件中

const _keyValueObject = useMemo(() => utilityFunction(array, object), [array, object])

const [keyValueObject, setKeyValueObject] = useState<SomeTransport>(_keyValueObject)

const handleStateChange = useCallback((id: number) => {
        setKeyValueObject(keyValueObject => {
            const temp = { ... keyValueObject }
            keyValueObject[id].isChecked = ! keyValueObject[id].isChecked
            return temp
        })
  }, [])

return( 
    <Container>
          {!! keyValueObject &&
               Object.values(keyValueObject).map(value => (
                   <ValueItem
                      key={value.id}
                      category={value}
                      handleStateChange ={handleStateChange}
                    />
               ))}
      </Container>
)

在子组件中 ValueItem

   const clickHandler = useCallback(
        event => {
            event.preventDefault()
            event.stopPropagation()
            handleStateChange(value.id)
        },
        [handleStateChange, value.id],
    )

return (
        <Container>
            <CheckBox checked={value.isChecked} onClick={clickHandler}>
                {value.isChecked && <Icon as={CheckboxCheckedIcon as AnyStyledComponent} />}
            </CheckBox>
            <CategoryItem key={value.id}>{value.title}</CategoryItem>
        </Container>
    )
    
export default ValueItem

在子组件中,如果我使用 export default memo(ValueItem),则复选框不会在单击时更新。

我现在需要的是不要重新渲染每个子组件,但要记住复选框有效。有什么建议吗?

Working Codesandbox

说明

您需要做的是用 React.memo 包裹 child。这样你就可以确保 Child 被记忆并且不会 re-render 不必要的。然而,这还不够。

在 parent 中,handleStateChange 在每次渲染时都会获得一个新的引用,因此它会生成 parent 渲染。如果 parent 呈现,则所有 children 将 re-render。用 useCallback 包装 handleStateChange 确保反应组件记住对函数的引用。 memo 记住了 Child 的结果。

Useful resource

传播 (const temp = { ... keyValueObject }) 并不像您想象的那样深度克隆对象。所以虽然 keyValueObject 会有一个新的引用,它的对象值不会被克隆,所以会有相同的引用,所以 memo 会认为在比较 category 属性时没有任何变化。

解决方案: 确保为要更新的 keyValueObject 的 ID 创建一个新值。示例:setKeyValueObject(keyValueObject => ({...keyValueObject, [id]: {...keyValueObject[id], isChecked: !keyValueObject[id].isChecked}))。现在 keyValueObject[id] 是一个新的 object/reference,因此 memo 会看到它并呈现您的组件。它不会渲染其他子项,因为它们的引用保持不变。