Помните, что функциональные компоненты - это просто функции, применяются все обычные вещи, которые вы знаете о функциях. Несмотря на то, что хуки делают некоторые кажущиеся маги c за кулисами (на самом деле это не маги c, просто у них есть контекстная информация, которую мы не видим), код в вашей функции компонента выполняется в соответствии с обычными правилами. Это означает, что этот код
const Comp = () => {
const [ws] = useState(socketIO());
// ... the rest of the component ...
};
будет всегда вызывать socketIO
, поэтому он может передать возвращаемое значение в useState
.
. Для вычисления только значения один раз , когда компонент создается впервые, используйте ссылку (useRef
) для представления информации об экземпляре, не являющейся состоянием (такая вещь, которую вы бы сохранили непосредственно в компоненте класса) например):
const Comp = () => {
const {current: instance} = useRef({});
const ws = instance.ws = instance.ws || socketIO();
// ... the rest of the component ...
};
{}
по-прежнему оценивается каждый раз, когда вызывается Comp
, но эти издержки тривиальны. Вы получаете от useRef
объект со свойством current
, относящимся к изменяемому объекту - объект, который всегда будет одинаковым в течение всего времени существования компонента. Вторая строка использует свойство ws
этого объекта, инициализируя его в первый раз, если оно ложно.
Это использование вызывается в useRef
документах :
Однако useRef()
полезен не только для атрибута ref. Это удобно для хранения любого изменяемого значения примерно так же, как вы используете поля экземпляров в классах.
Вот пример с заменой для socketIO
:
const { useRef, useState } = React;
function socketIO() {
console.log("socketIO called");
return {};
}
const Comp = () => {
console.log("Comp called");
const {current: instance} = useRef({});
const ws = instance.ws = instance.ws || socketIO();
const [counter, setCounter] = useState(0);
return (
<div>
{counter} <input type="button" value="+" onClick={() => setCounter(c => c + 1)} />
</div>
);
};
ReactDOM.render(<Comp/>, document.getElementById("root"));
<div id="root"></div>
<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>
Я должен отметить, что заманчиво использовать useMemo
здесь, но useMemo
не гарантирует, что ваша функция будет создавать запомненный результат не будет вызван во второй раз. useMemo
для оптимизации производительности, а не семантики. Начиная с документов :
Вы можете полагаться на использованиеMemo в качестве оптимизации производительности, а не в качестве гарантии c. В будущем React может выберите «забыть» некоторые ранее запомненные значения и пересчитать их при следующем рендере, например, чтобы освободить память для компонентов вне экрана. Напишите свой код, чтобы он по-прежнему работал без useMemo
, а затем добавьте его для оптимизации производительности.
(их выделение)