简体   繁体   English

更新父状态时更新子组件

[英]Update child component when parent state is updated

When there is a new update in my parent component, which occur within the function updateItemValue() , the items state will only get updated in parent.当我的父组件中有新的更新时,它发生在function updateItemValue()items状态只会在父组件中更新。 The child component won't get updated in an instance.子组件不会在实例中更新。 It only get updated when the updateItemValue() get triggered twice.它只有在updateItemValue()被触发两次时才会更新。

It's something like:它是这样的:

  • 1st round, parent update the items, child didn't receive the update.第一轮,父母更新项目,孩子没有收到更新。
  • 2nd round, parent update the items, child only receive the update from 1st round第二轮,父母更新项目,孩子只收到第一轮的更新

Parent Component父组件

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

Child Component子组件

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 : the state items came from redux state. p/s :状态items来自 redux 状态。 So the state update at updateItemValue() is to add some extra value which I want to use at child component.所以updateItemValue()的状态更新是添加一些我想在子组件中使用的额外值。

Issue问题

You are mutating an object reference你正在改变一个对象引用

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

Solution解决方案

Looks like you are using Redux since you are using the useSelector react hook.看起来您正在使用 Redux,因为您使用的是useSelector反应钩子。

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

I'll assume you have access to a useDispatch hook and action to dispatch to update your state as well.我假设您也可以访问useDispatch钩子和要调度的操作来更新您的状态。

You can likely simplify your component code a bit, and move the update logic to the reducer so stat is correctly updated.您可能会稍微简化您的组件代码,并将更新逻辑移动到 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>
  );
};

I'm going to just guess at the reducer function now, it may be similar to the following.我现在只是猜测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;
  }
};

Additional Suggestion附加建议

You can also simplify the child component ItemCard a bit.您还可以ItemCard简化子组件ItemCard Direct attach the click handler and simplify the conditional rendering.直接附加点击处理程序并简化条件渲染。 Also, I don't know if it was intentional ( I think not ), but you may not want the string literal "<p>Extra item is required</p>" rendering.另外,我不知道这是否是故意的(我认为不是),但您可能不希望字符串文字"<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>
  );
}

And update the mapping in the parent.并更新父级中的映射。 Don't pass an id prop, but pass the item._id to the updateSelectedItem callback.不要传递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