更新父状态时更新子组件

Posted

技术标签:

【中文标题】更新父状态时更新子组件【英文标题】:Update child component when parent state is updated 【发布时间】:2021-03-02 13:44:13 【问题描述】:

当我的父组件中有新的更新时,发生在 function updateItemValue() 中,items 状态只会在父组件中更新。子组件不会在实例中更新。仅当updateItemValue() 被触发两次时才会更新。

有点像:

第一轮,家长更新项目,孩子没有收到更新。 第二轮,家长更新项目,孩子只收到第一轮的更新

父组件

const SelectItem = () => 
    const  items  = useSelector(state => state.itemsReducer);
    const [ itemUpdate, setItemUpdate ] = useState(false);
    const [ group_id, setGroupId ] = useState(false);

    useEffect(() => 
        updateItemValue()
    , [itemUpdate])
     
    function updateItemValue()
        // original items value
        /*
            [
                "_id":1, "name":"Item 1", "description":"Item 1 description",
                "_id":2, "name":"Item 2", "description":"Item 2 description",
                "_id":3, "name":"Item 3", "description":"Item 3 description"
            ]
        */
    
        // step 3 : now this function get called because the group_id is updated
        items.forEach((item, i) => 
            if (group_id == item.group_id) 
                // step 4 : update the value of items state, add extra data, once this updated, I expect the new items value to be passed to child component
                items[i].extra = true;
            
        );
    
    
    // step 2 : update the group_id and set the itemUpdate to true to trigger useEffect
    function selectGroup(group_id)
        setItemUpdate(true)
        setGroupId(group_id)
    
    
    return(
        <div>
            /* step 1: select option */
            <button onClick=() => selectGroup(1)>Option 1</button>
            <button onClick=() => selectGroup(2)>Option 2</button>
            
                items.map((item) => (
                    <ItemCard
                      key=item._id
                      id=item._id
                      name=item.name
                      description=item.description
                      onClick=(value) => updateSelectedItem(value)
                    />
                ))
            
        </div>
    )

子组件

export default function ItemCard(id, name, description, onClick, extra)
    console.log(extra)  // the value is undefined when selectGroup() 1st triggered from parent
    return (
        <div onClick=() => onClick(id)>
          <div>
            <p>name</p>
            <p>description</p>
             extra ? '<p>Extra item is required</p>' : null 
          </div>
        </div>
    )

p/s : 状态 items 来自 redux 状态。所以updateItemValue() 的状态更新是添加一些我想在子组件中使用的额外值。

【问题讨论】:

您能否更新问题以包含更多Minimal, Complete, and Reproducible 组件代码示例? “子组件”不是有效的反应组件。我们是否假设“子组件”是ItemCarditemUpdate 是什么? updateSelectedItem 在哪里定义?什么叫updateItemValue 我已经更新了我的代码。 我不知道items 是什么,但是您正在对其进行变异,因此父组件可能会看到相同的对象引用,因此不知道要重新渲染。你能分享一下items是什么,在哪里定义的吗? @DrewReese 对此感到抱歉。我忘了包括它。请再次检查最新更新。谢谢! 【参考方案1】:

问题

你正在改变一个对象引用

function updateItemValue()
  items.forEach((item, i) => 
    if (group_id == item.group_id) 
      items[i].extra = true; // <-- state object mutation!
    
  );

解决方案

看起来您正在使用 Redux,因为您使用的是 useSelector react 钩子。

const  items  = useSelector(state => state.itemsReducer);

我假设您也可以访问 useDispatch 挂钩和操作来调度以更新您的状态。

您可能会稍微简化您的组件代码,并将更新逻辑移至 reducer,以便正确更新 stat。

const SelectItem = () => 
  const  items  = useSelector((state) => state.itemsReducer);
  const dispatch = useDispatch();

  // step 2: dispatch action to update state via the group_id
  function selectGroup(groupId) 
    dispatch( type: "UPDATE_GROUP", groupId );
  

  return (
    <div>
      /* step 1: select option */
      <button onClick=() => selectGroup(1)>Option 1</button>
      <button onClick=() => selectGroup(2)>Option 2</button>
      items.map((item) => (
        <ItemCard
          key=item._id
          id=item._id
          name=item.name
          description=item.description
          onClick=(value) => updateSelectedItem(value)
        />
      ))
    </div>
  );
;

我现在只是猜测一下reducer函数,它可能类似于以下。

const initialState = 
  items: [],


const itemsReducer = (state = initialState, action) => 
  switch (action.type) 

    ...

    // step 3: update items array by matching item group id
    case "UPDATE_GROUP":
      return 
        ...state,
        items: state.items.map((item) =>
          item.group_id === action.groupId
            ? 
                ...item,
                extra: true
              
            : item
        )
      ;

    ...

    default:
      return state;
  
;

补充建议

您还可以稍微简化子组件ItemCard。直接附加点击处理程序并简化条件渲染。另外,我不知道这是否是故意的(我认为不是),但您可能不希望字符串文字 "&lt;p&gt;Extra item is required&lt;/p&gt;" 呈现。

function ItemCard( name, description, onClick, extra ) 
  return (
    <div onClick=onClick>
      <div>
        <p>name</p>
        <p>description</p>
        extra && <p>Extra item is required</p>
      </div>
    </div>
  );

并更新父级中的映射。不要传递 id 属性,而是将 item._id 传递给 updateSelectedItem 回调。

items.map((item) => (
  <ItemCard
    key=item._id
    name=item.name
    description=item.description
    onClick=() => updateSelectedItem(item._id)
  />
))

【讨论】:

感谢您的详细解释:)

以上是关于更新父状态时更新子组件的主要内容,如果未能解决你的问题,请参考以下文章

防止子级在更新父级状态时重新渲染,使用父级方法作为本机反应的道具

通过父组件的onClick更新组件状态

根据父状态更新子状态 react 功能组件

反应无状态子组件不会更新父组件中的状态更改

React 子组件在父组件中更新状态后如何将其重置回原始状态

父状态更改后反应子组件未更新