当我的父组件中有一个新的更新发生在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:状态CCD_ 4来自redux状态。所以updateItemValue()
的状态更新是添加一些额外的值,我想在子组件中使用这些值。
问题
您正在更改对象引用
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
。直接附加单击处理程序并简化条件呈现。此外,我不知道这是否是故意的(我认为不是(,但您可能不想要字符串文字"<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)}
/>
))}