我目前正在使用Hooks构建React应用。 我有一个组件( View ),该组件可以从某处加载数据并动态生成组件( Clicker,ClickerDisplay ),一方面显示数据,另一方面显示数据替换数据。
该应用程序基本上可以运行,但我担心的是它的性能。我的状态每100毫秒更新一次,如果更改后所有内容都重新呈现,那将是一个很大的开销。
是否可以仅重新渲染应更改的组件?
对我来说,另一件事似乎很丑陋,那就是我更新状态的方式。 setState([...state]);
有更好的方法吗?
旁注:
在我的示例代码ID中,当前仅将项目属性传递给子组件,但我打算将整个项目传递给它们。
我还想保留父组件中的主要状态,因为应将应用程序的状态构建并保存到一个大的JSON对象中。
数据
//Some Random Data
const things = [
{
"id": 0,
"name": "thing 1",
},
{
"id": 1,
"name": "thing 2",
},
{
"id": 2,
"name": "thing 3",
}
];
View.js
export default function View(props) {
const tmp = things.map(item => {
return { ...item, amount: 0 };
});
const [state, setState] = useState(tmp);
//alternates the amount and updates the state
function updateAmount(item, newAmount) {
item.amount = newAmount;
setState([...state]);
}
function createClicker(item) {
const { name, amount } = item;
return (<Clicker
name={name}
amount={amount}
clicked={() => updateAmount(item, amount + 1)} />
);
}
function createClickerDisplay(item) {
const { name, amount } = item;
return (<ClickerDisplay
name={name}
amount={amount} />
);
}
return (
<ul>
{state.map(item =>
<li>
{createClicker(item)} = {createClickerDisplay(item)}
</li>
)}
</ul>
);
}
Clicker.js
//component to alternate data
function Clicker(props) {
const { clicked, name, amount } = props;
return <button onClick={() => clicked(name)}>{name}: {amount}</button>;
}
ClickerDisplay.js
//component to display data
function ClickerDisplay(props) {
const { name, amount } = props;
return <span>{name}: {amount}</span>;
}
答案 0 :(得分:1)
除非配置为根据状态和道具的特定更改有条件地重新渲染,否则反应组件将在每个生命周期中重新渲染。在您的情况下,当您使用功能组件时,可以利用React.memo()
来记住这些组件,这意味着只有在传递的prop内的值发生更改时才会呈现。尽管要注意复杂的结构,但是要小心,默认比较器只会做浅比较。
下面的代码段说明了记忆化组件的用法,以及解决了您作为点击事件处理程序一部分执行的状态突变。
const things = [
{ "id": 0, "name": "thing 1" },
{ "id": 1, "name": "thing 2" },
{ "id": 2, "name": "thing 3"}
];
function Clicker({ clicked, name, amount }) {
return <button onClick={() => clicked(name)}>{name}: {amount}</button>;
}
// Memoized component that only updates when its simple props change
const ClickerDisplayMemoized = React.memo(function ClickerDisplay(props) {
const { name, amount, index } = props;
console.log(`Updating ClickerDisplay ${index + 1}`);
return <span>{name}: {amount}</span>;
});
function View() {
const thingsWithAmounts = things.map((item) => ({ ...item, amount: 0 }));
const [state, setState] = React.useState(thingsWithAmounts);
// Updated callback that avoids state mutation
function updateAmount(index, newAmount) {
setState(state.map((item, i) => ({
...item,
amount: (i === index ? newAmount : item.amount)
})));
}
const createClicker = ({ name, amount }, index) => (
<Clicker name={name} amount={amount}
clicked={() => updateAmount(index, amount + 1)} />
);
const createClickerDisplay = ({ name, amount }, index) => (
<ClickerDisplayMemoized name={name} amount={amount} index={ index } />
);
return (
<ul>
{state.map((item, i) => (
// this component needs a key as it's part of a map()
<li key={ i }>
{createClicker(item, i)} = {createClickerDisplay(item, i)}
</li>
))}
</ul>
);
}
ReactDOM.render(<View />, document.getElementById('root'));
/* this is just so the console in this snippet doesn't cover the components */
.as-console-wrapper { max-height: 50% !important; }
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>