Если вы запомнили кнопку, вы не испытываете такого поведения.
В частности, это:
const Button = memo(({ incrementCounter }) => {
const renderCount = useRef(1);
console.log("button rendered: ", renderCount.current);
renderCount.current++;
return <button onClick={incrementCounter}>Increment</button>;
});
CodeSandbox Mirror:
Обновление
Если вы хотите что-то из документов первое предложение расскажет вам, почему это происходит. Я знаю, что для setState
, но та же самая концепция следует за крючком useState
, но документы для такого рода отстой. Вы можете проверить эту часть документов, особенно там, где написано "Строка 9:" ...
Помните, что когда состояние изменяется в компоненте X, компонент X перерисовывается, вместе со всеми своими детьми. Я знаю, что React говорит вам «поднимать состояние вверх», чего я никогда не понимал, потому что поднятие состояния вверх приводит к чрезмерной загрузке повторных визуализаций.
Именно поэтому кнопка выполняет повторную визуализацию .. потому что состояние меняется в его родителя. Родитель (<App />
) изменил свое состояние counter
, что вызывает повторную визуализацию компонента <App />
и его дочерних элементов, включая <Button />
.
По моему мнению, React трудно контролировать состояние и повторный рендеринг, с которым Redux может помочь, но в целом такие вещи, как memo
, useCallback
, et c .. все это кажется мне бинтами. Если вы поместите свое состояние в неправильный компонент, у вас будет плохое время.
Оборачивание <Button />
компонента в memo
в основном говорит: если этот компонент имеет parent (в нашем случае <App />
), и этот родитель перерисовывает, я хочу посмотреть на все наши реквизиты, и если наши реквизиты не изменились по сравнению с тем, что мы получили в прошлый раз, не перерисовывать. По сути, только повторно сделать, если наши реквизиты меняются. Вот почему memo
исправляет это .. потому что функция, которую мы используем для обработки реквизита incrementCounter
, не изменяется - она остается постоянной.
Я добавил несколько примеров ниже, демонстрирующих это.
Оригинальный ответ / фрагмент:
const { memo, useState, useCallback, useEffect, useRef } = React;
const { render } = ReactDOM;
const App = () => {
const [counter, setCounter] = useState(0);
const incrementCounter = useCallback(() => {
setCounter(c => c + 1);
}, [setCounter]);
useEffect(() => {
console.log("increment changed!");
}, [incrementCounter]);
return (
<div>
<CountValue counter={counter} />
<Button incrementCounter={incrementCounter} />
</div>
);
}
const CountValue = ({ counter }) => {
return <div>Count value: {counter}</div>;
};
const Button = memo(({ incrementCounter }) => {
const renderCount = useRef(1);
console.log("button rendered: ", renderCount.current);
renderCount.current++;
return <button onClick={incrementCounter}>Increment</button>
});
render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
SNIPPET # 2:
Этот фрагмент показывает, как все, а не просто кнопка, перерисовывается.
const { useState, useEffect } = React;
const { render } = ReactDOM;
const App = () => {
console.log("App rendered");
const [counter, setCounter] = useState(0);
const incrementCounter = () => setCounter(c => c + 1);
useEffect(() => {
console.log(" - Increment fired!");
console.log();
}, [incrementCounter]);
return (
<div>
<CountValue counter={counter} />
<Button incrementCounter={incrementCounter} />
<p>Open console</p>
</div>
);
}
const CountValue = ({ counter }) => {
console.log("CountValue rendered");
return <div>Count value: {counter}</div>;
};
const Button = ({ incrementCounter }) => {
console.log("Button rendered");
return <button onClick={incrementCounter}>Increment</button>
};
render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
SNIPPET # 3:
Этот фрагмент показывает, как вы двигаетесь состояние, и т. д. c .. в компонент <CountValue />
, компонент <App />
не выполняет повторную визуализацию ..
const { useState, useEffect } = React;
const { render } = ReactDOM;
const App = () => {
console.log("App rendered");
return (
<div>
<CountValue />
<p>Open console</p>
</div>
);
}
const CountValue = () => {
console.log("CountValue rendered");
const [counter, setCounter] = useState(0);
const incrementCounter = () => setCounter(c => c + 1);
return (
<div>
<div>Count value: {counter}</div>
<Button incrementCounter={incrementCounter} />
</div>
);
};
const Button = ({ incrementCounter }) => {
console.log("Button rendered");
console.log();
return <button onClick={incrementCounter}>Increment</button>
};
render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
SNIPPET # 4:
Этот фрагмент больше похож на мысль эксперимент, который показывает, как использовать рендер реквизит.
const { useState, useEffect } = React;
const { render } = ReactDOM;
const App = () => {
console.log("App rendered");
return (
<div>
<CountValue present={({ increment, counter }) => {
return (
<div><Button incrementCounter={() => increment()} />
<p>Counter Value: {counter}</p></div>
)
}} />
<p>Open console</p>
</div>
);
}
const CountValue = ({ present }) => {
const [counter, setCounter] = useState(0);
const increment = () => {
setCounter(c => c + 1);
}
console.log("CountValue rendered");
return (
<React.Fragment>
{present({ increment, counter })}
</React.Fragment>
);
};
const Button = ({ incrementCounter }) => {
console.log("Button rendered");
return <button onClick={incrementCounter}>Increment</button>
};
render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
SNIPPET # 5:
Похоже, это то, что вы 'after after .. Это только повторно визуализирует CountValue. Это достигается путем передачи метода setCounter
, который создается useState
, до родительского объекта через объект обратного вызова.
This способ, которым родитель может манипулировать состоянием, без необходимости фактически удерживать состояние.
const { useState, useEffect } = React;
const { render } = ReactDOM;
const App = () => {
console.log("App rendered");
let increaseCount;
return (
<div>
<CountValue callback={({increment}) => increaseCount = increment} />
<Button incrementCounter={() => increaseCount()} />
<p>Open console</p>
</div>
);
}
const CountValue = ({ callback }) => {
console.log("CountValue rendered");
const [counter, setCounter] = useState(0);
callback && callback({
increment: () => setCounter(c => c + 1)
});
return <p>Counter Value: {counter}</p>;
};
const Button = ({ incrementCounter }) => {
console.log("Button rendered");
return <button onClick={incrementCounter}>Increment</button>
};
render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>