我刚刚启动React,在此项目列表教程中,我对更新项目状态有一些疑问。另外,我正在使用功能组件。因此在app.js中
const [items, setItems] = useState([
{
id: 1,
title: 'Banana',
bought: false
},
...
])
然后我在app.js中有一个函数,当我选中一个复选框时会将购买的商品更新为true或false
// The id is passed in from Item.js down below
const markBought = (id) => {
setItems(
items.map(
item => {
if (item.id === id) {
/// If bought is false, checking it will make it true and vice versa
item.bought = !item.bought; // (1)
}
return item; // (2)
})
);
};
return (
<div className="App">
<Items items={items} markBought={markBought}></Items>
</div>
);
老师说我们正在使用一种叫做“组件钻探”的方法。因此,在Items.js中,我们会映射每个项目以逐一显示它们,但我认为不必显示它。
最后在Item.js中
<input type="checkbox" onChange={() => props.markBought(props.item.id)} />
{props.item.title}
该应用程序运行良好,但对我来说有点令人困惑。所以:
很抱歉,如果这有点长,我们将不胜感激。感谢您的阅读
答案 0 :(得分:0)
您好,欢迎来到Stackoverflow。
您总是返回item
。您只有一个if
语句,它将更改购买状态,即使以上条件为false,也将返回item
,这是正确的方法。
Map确实不会修改数组,但会返回一个新数组。如果要获取该返回数组,则可以执行以下操作:
const myNewArray = items.map(...)
这个新的array
到达您的其他组件的方式是因为这个新的array
被赋予了您的useState()
。
您看到setItems()
吗?这将设置您的状态,并且Item.js
将自动更新。
那真是太棒了。 state
的所有组件将在此state
更新后进行更新。
答案 1 :(得分:0)
您的地图函数总是返回一个项目。如果您要修改的项目与当前正在映射的项目的ID相匹配,它只会首先修改该项目。 Map会返回一个新的项目数组(即使它不做任何更改),这会使useState看到一个新值。默认情况下,在React中,检查更新不是很聪明-只是检查是否oldValue === newValue
。
对于诸如字符串之类的基元,只要两个对象的值匹配,对象相等性测试就会返回true。
"foo" === "foo" // => true
但是,这不适用于对象或数组。包含相同值的两个不同数组将不进行相等比较(因为Javascript不比较它们的内容,而是对象的ID):
["foo"] === ["foo"] // => false
因此,当您map
items
时,您将获得一个新的数组对象(因为调用:map
将回调函数的返回值收集到一个新的数组中),这将从不匹配items
的先前值,因此每次调用setItems
都会使React说“ hm,我先前的items
与该对象不同我的新items
,我必须重新渲染此组件”。
答案 2 :(得分:0)
您正在对地图中的项目进行突变,如果您将Item组件优化为纯组件,则由于发生突变,该组件将不会重新呈现。请尝试以下操作:
//use useCallback so marBought doesn't change and cause
// needless DOM re renders
const markBought = useCallback(id => {
setItems((
items //pass callback to the setter from useState
) =>
items.map(
item =>
item.id === id
? { ...item, bought: !item.bought } //copy item with changed value
: item //not this item, just return the item
)
);
}, []);
这是一个完整的例子:
const { useCallback, useState, useRef, memo } = React;
function Items() {
const [items, setItems] = useState([
{
id: 1,
title: 'Banana',
bought: false,
},
{
id: 2,
title: 'Peach',
bought: false,
},
]);
const toggleBought = useCallback(id => {
setItems((
items //pass callback to the setter from useState
) =>
items.map(
item =>
item.id === id
? { ...item, bought: !item.bought } //copy item with changed value
: item //not this item, just return the item
)
);
}, []);
return (
<div>
{items.map(item => (
<Item
key={item.id}
item={item}
toggleBought={toggleBought}
/>
))}
</div>
);
}
//use memo to make Item a pure component
const Item = memo(function Item({ item, toggleBought }) {
const renderedRef = useRef(0);
renderedRef.current++;
return (
<div>
<div>{item.title}</div>
<div>bought: {item.bought ? 'yes' : 'no'}</div>
<button onClick={() => toggleBought(item.id)}>
toggle bought
</button>
<div>Rendered: {renderedRef.current} times</div>
</div>
);
});
//render the application
ReactDOM.render(<Items />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
这是一个残破的示例,其中您更改了项目并且即使状态确实发生了变化也看不到重新渲染:
const { useCallback, useState, useRef, memo } = React;
function Items() {
const [items, setItems] = useState([
{
id: 1,
title: 'Banana',
bought: false,
},
{
id: 2,
title: 'Peach',
bought: false,
},
]);
const toggleBought = useCallback(id => {
setItems((
items //pass callback to the setter from useState
) =>
items.map(
item =>
item.id === id
? ((item.bought = !item.bought),item) //mutate item
: item //not this item, just return the item
)
);
}, []);
return (
<div>
<div>
{items.map(item => (
<Item
key={item.id}
item={item}
toggleBought={toggleBought}
/>
))}
</div>
<div>{JSON.stringify(items)}</div>
</div>
);
}
//use memo to make Item a pure component
const Item = memo(function Item({ item, toggleBought }) {
const renderedRef = useRef(0);
renderedRef.current++;
return (
<div>
<div>{item.title}</div>
<div>bought: {item.bought ? 'yes' : 'no'}</div>
<button onClick={() => toggleBought(item.id)}>
toggle bought
</button>
<div>Rendered: {renderedRef.current} times</div>
</div>
);
});
//render the application
ReactDOM.render(<Items />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>