React: отрисовка только необходимых компонент
Есть React компонент WordsEditor
, который содержит сотни компонент WordBlock
.
При изменении состояний WordsEditor
происходит его перерисовка и, соответственно, перерисовка всем его компонент WordBlock
, при том, что изменяется лишь несколько компонент WordBlock
.
Подскажите как сделать так, чтобы перерисовывались лишь необходимые компоненты.
WordsEditor:
// компонент: 'Редактор слов'
const WordsEditor: React.FC<IPropsWordsEditor> = (props) => {
// контроль состояний
const [selectedIndices, setSelectedIndices] = useState<number[]>([]); // список индексов выделенных слов
const [selectedAuxIndices, setSelectedAuxIndices] = useState<number[]>([]); // список вспомогательных индексов выделенных слов
const [categoriesGroups, setCategoriesGroups] = useState<IDataCategoryGroup[]>([]); // список сформированных групп слов
// ОСНОВНАЯ ЛОГИКА
// ...
// отрисовать компонент
const toolbarIndex: number = toolbar_enabled && selectedIndices.length > 0 ? Math.min(...selectedIndices) : -1;
const content: any = words.map((wordInfo, index) => {
if (wordInfo.active) {
const wordBlock: any = <WordBlock
key = {index}
index = {index}
word = {wordInfo.word}
main = {selectedIndices.includes(index)}
aux = {selectedAuxIndices.includes(index)}
type = {categoriesGroups.find(elem => elem.ids.includes(index))?.sentiment ?? ''}
/>;
if (index === toolbarIndex)
return (
<ToolbarPanel
key = {index}
selected = {selectedIndices}
groups = {categoriesGroups}
onClose = {handleDisableSelections}
onDisableTones = {handleDisableTonesSelections}
onRegroup = {handleRegroupSelections}
onCategoryze = {handleCategoryzeSelections}
>
{wordBlock}
</ToolbarPanel>
);
else
return wordBlock;
}
return wordInfo.word;
});
return (
<div className='words-editor' onClick={handleWordsEditorClick}>
{content}
</div>
);
}
WordBlock:
// компонент: 'Слово'
const WordBlock: React.FC<IPropsWordBlock> = (props) => {
console.log('word');
// функция определяющая тип класса
function prepareClassName() {
// определить название классов для разных состояний слова
const main: string = props.main ? 'selected' : '';
const aux: string = props.aux ? 'selected-aux' : '';
const type: string = props.type;
// сформировать классы
return [(main !== '') ? main : type, aux].join(' ').trim();
}
// отрисовать компонент
return (
<span
data-index = {props.index}
className = {prepareClassName()}
>
{props.word}
</span>
);
}
Ответы (1 шт):
При изменении состояний WordsEditor происходит его перерисовка и, соответственно, перерисовка всем его компонент WordBlock, при том, что изменяется лишь несколько компонент WordBlock. Подскажите как сделать так, чтобы перерисовывались лишь необходимые компоненты.
Можно применить меморизацию компонента. Т.о., если пропсы не менялись - компонент рендериться не будет.
const Itm = React.memo(props => {
const d = props.data
const test = d.test ? d.test.toString() : '?'
console.log('Рендер', d.id)
return <li>
{d.name} - {test}
</li>
})
//
function App() {
const [itm, setItm] = React.useState(null)
const [arr, setArr] = React.useState([])
React.useEffect(_ => {
const url = 'https://jsonplaceholder.typicode.com/users'
fetch(url)
.then(r => r.json())
.then(a => {
setArr(a)
})
.catch(console.error)
}, [])
const act = _ => {
const i = Math.floor(Math.random() * arr.length)
console.log('Меняем только', i)
setItm(i)
setArr(old => old.map((o, j) => i == j ? {...o, test: new Date()} : o))
}
console.log('Рендер App')
return <section>
{arr.length ? <button onClick={act}>Тест</button> : 'Получение данных...'}
<ul>
{arr.map(o => <Itm key={o.id} data={o} />)}
</ul>
</section>
}
const domContainer = document.querySelector('#like_button_container');
const root = ReactDOM.createRoot(domContainer);
root.render(<App />);
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<div id="like_button_container"></div>