(pro React) Dlouhé seznamy se obvykle optimalizují tak, že se pro položky udělá samostatná komponenta, tato bude navíc memoizovaná pomocí React.memo a při vykreslování seznamu se každé položce nastaví key. Díky té memoizaci se to renderování skutečně uřízne před voláním render-u na jednotlivých položkách (když se jim nezměnily propsy).
V upraveném příkladu se tedy bude opakovaně renderovat jenom první řádek (a App samozřejmě).
class A0 extends React.Component {
constructor(props) {
super();
console.log("Constructor called ");
this.state = {
input: 1
};
}
render() {
console.log("A render, props: " + JSON.stringify(this.props));
return <div>Input {this.state.input}</div>;
}
}
const A = React.memo(A0);
class App extends React.Component {
i = 0;
constructor() {
super();
this.state = {
arr: [
{ id: 0, val: 0 },
{ id: 1, val: 0 },
{ id: 2, val: 0 }
]
};
}
componentDidMount() {
setInterval(
() =>
this.setState({
arr: this.state.arr.map((item) =>
item.id === 0 ? { id: 0, val: this.i++ } : item
)
}),
1000
);
}
render() {
console.log("App render");
return (
<div>
{this.state.arr.map((i) => (
<A key={i.id} data={i} />
))}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("container"));
Ano, zkousel jsem memo, a je to jediny zpusob, ktery zabrani vykresleni komponentypri updatu nadrazeneho stavu. Akorat jsem to nepouzil, protoze jsem si rozdelil FoodEntries do komponent FoodEntryDay, a pridal jsem tlacitko Ulozit, takze ted uz nemusi kazda zmena vyvolat update cele tabulky, ale jen FoodEntryDay, navic zbytecnym volanim metod jsem zabranil pres useState, kdy se mi dela diff na vstupni props.
Bohuzel vsak me to stalo duplikovani props do vnitrniho stavu.
Ale co je zajimave, tak ikdyz udelam:
const FoodEntriesTableDayRow = (props) => {
const [totalCalories, setTotalCalories] = useState(FoodEntry.getTotalCalories(props.foodEntry));
useEffect(() => {
console.log(FoodEntriesTableDayRow.name, useEffect.name)
setTotalCalories(FoodEntry.getTotalCalories(props.foodEntry))
}, [props.foodEntry])
const called = () => {
console.log('Called!')
}
return (
<tr>
<td><input type="date" value={props.foodEntry.date}/></td>
<td><Searchbox value={props.foodEntry.food.name}/></td>
<td>
<input type="number"
value={props.foodEntry.amount}
step={20}
onChange={(val) =>
props.onChange({...props.foodEntry, amount: val.target.value})
}/>
</td>
<td>{props.foodEntry.food.calories}</td>
<td>{totalCalories}</td>
<td>
<input type="button" value="-" onClick={() => props.onRemove({...props.foodEntry})}/>
</td>
</tr>
)
}
Konkretneji:
useEffect(() => {
console.log(FoodEntriesTableDayRow.name, useEffect.name)
setTotalCalories(FoodEntry.getTotalCalories(props.foodEntry))
}, [props.foodEntry])
Jeste kokretneji:
}, [props.foodEntry])
Tak se mi sice neprovede funkce useEffect, kdyz se nikterak nezmenil props.foodEntry, ale stale se mi zbytecne zavola testovaci metoda called() volana z renderingu. Tomu dokaze zabranit jedine to Memo. Moc z toho nejsem moudry, proc React tu Render funkci znovu zavola, protoze nejsem uplne presvedcen, ze Memo je spravny zpusob reseni. Perfromance issues s tim nemam, jen mi to prijde jako skoda ze to React dela, a nechapu proc.