繁体   English   中英

更新父状态时更新子组件

[英]Update child component when parent state is updated

当我的父组件中有新的更新时,它发生在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()的状态更新是添加一些我想在子组件中使用的额外值。

问题

你正在改变一个对象引用

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

解决方案

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

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简化子组件ItemCard 直接附加点击处理程序并简化条件渲染。 另外,我不知道这是否是故意的(我认为不是),但您可能不希望字符串文字"<p>Extra item is required</p>"呈现。

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)}
  />
))}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM