Я играл с React несколько лет, но в некоторых случаях до сих пор путаюсь с механизмом монтирования/размонтирования.
Поскольку mount/unmount — это место для выполнения побочного эффекта, я не хочу, чтобы они вызывались случайным образом. Поэтому мне нужно выяснить, как они работают. Насколько я понимаю в настоящее время, когда виртуальный дом не присутствует в реальном доме, он, как правило, размонтируется. Однако, похоже, это не вся история, и я не могу об этом рассуждать.
function TestMount(props) {
useEffect(() => {
console.info("componentDidMount", props.name);
return () => {
console.info("componentWillUnount", props.name);
};
}, []);
return <h1>Test content {" " + JSON.stringify(props.name)}</h1>;
}
function Update({ click }) {
return <button onClick = {click}>Update</button>;
}
function App() {
const [count, setCount] = useState(0);
const Component = name => <TestMount name = {name} />;
return (
<div className = "App">
<h1>{count}</h1>
<Component name = "one" />
{Component("two")}
<Update click = {() => setCount(x => x + 1)} />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Первый компонент перемонтирует рендеринг приложения, а второй нет? Почему это происходит?
@zerkms не видит, как это связано с механизмом монтирования в реакции
@zerkms Что действительно актуально, так это (()=>'one')!==(()=>'one'),но 'two'==='two'
Я не уверен, почему вы так думаете, у вас есть {name: 'one'} реквизит, и я не вижу, где у вас есть анонимная функция () => 'one'
@zerkms ()=>'one' на самом деле является легальным компонентом реакции, другой ()=>'one' является компонентом реакции еще один, если он имеет другую идентичность. Если для создания элемента инициализируются разные компоненты, реакция отключит первый компонент и перемонтирует второй компонент вместо обновления «того же»
Не уверен, насколько это относится к вашему вопросу. У вас есть {name: 'one'} реквизиты в опубликованном вами коде (в этом выражении: <Component name = "one" />). Я не уверен, что вижу ()=>'one' где-нибудь в вашем коде. Важно то, что вы на самом деле пройти{name: 'one'}.





Component — это новая функция каждый раз, когда App визуализируется, поэтому <Component name = "one" /> тоже каждый раз перемонтируется, они считаются разными компонентами.
Результатом вызова Component("two") является <TestMount name = {"two"} />, TestMount остается неизменным при каждом рендеринге App, поэтому он не перемонтируется.
Component является недопустимым компонентом для того, для чего он используется, для передачи name строки в качестве name реквизита для TestMount компонента, потому что параметр name не является строкой, а объект реквизита, когда Component используется как <Component name = "one" />. name => <TestMount name = {name} /> — это функция рендеринга, для ясности предпочтительнее назвать его соответственно, например, renderTestMount, потому что компоненты не должны вызываться напрямую, как Component("two").
В случае, если предполагается, что функция используется как компонент или функция рендеринга взаимозаменяемо, сигнатура должна быть изменена на ({ name }) => <TestMount name = {name} />.
Ожидаемое поведение может быть достигнуто для <Component name = "one" /> путем запоминания Component:
const Component = useCallback(({ name }) => <TestMount name = {name} />, []);
Но так как Component не зависит от области App, правильный способ — определить его снаружи:
const Component = ({ name }) => <TestMount name = {name} />;
function App() {...}
Например, по этой причине React Router Route имеет отдельные реквизиты component и render для компонента и функция рендеринга. Это позволяет предотвратить ненужные перемонтирования для компонентов маршрута, которые необходимо определять динамически в текущей области.
Спасибо за ваш ответ, разница между render и component в react-router внезапно стала для меня понятна. Суть проблемы в том, что React хорош в element, а не в Component, просто обратите внимание, что вы не можете useCallback снаружи App из-за ограничений хука, lodash.once должно быть то, что вы имеете в виду
Пожалуйста. Вам не нужен useCallback вне приложения, компонент уже остается неизменным между рендерами, таким образом, это обычный компонент.
«Компонент является недопустимым компонентом, потому что он принимает имя в качестве аргумента вместо реквизита» --- как так? Это функция, и у нее могут быть аргументы с любым именем, будь то name или foo.
@zerkms Параметр может иметь любое имя, но то, что это name, предполагает, что он должен получить строку имени в качестве аргумента, например Component("two"), в то время как он получает объект реквизита, когда он используется в качестве компонента.
@estus это не делает это неверный: 1) Имя аргумента ничего не меняет 2) Можно передать объект как свойство компонента реакции. Я признаю, однако, что неверно истолковываю термин неверный.
@zerkms <Component name = "one" /> и {Component("two")} не взаимозаменяемы. Для намерения OP недопустимо, чтобы TestMount получал строку как name опору. Я обновлю ответ, чтобы прояснить ситуацию.
@estus верно. Первоначально казалось, что это недопустимо с точки зрения библиотеки реагирования, извините.
React умный с element не ComponentКомпонент — это шаблон, используемый для создания элемента с помощью операции <>. На мой взгляд, <> очень похож на new оператора в мире ООП.
Каждый раз, когда вызывается метод render (или функциональный компонент). Новый элемент создается с помощью <>, однако React достаточно умен, чтобы сказать, что элемент, созданный между рендерами, на самом деле один и тот же, то есть он был создан ранее и может быть повторно использован пока элемент создается одним и тем же компонентом
Однако, когда идентификация компонента, используемого для создания элемента, изменяется (даже если компоненты выглядят одинаково), React считает, что появилось что-то новое, поэтому он удаляет (размонтирует) предыдущий элемент и добавляет (монтирует) новый. Таким образом, вызывается componentDidMount или componentWillUnmount.
Думаю, у нас есть Component, и когда мы генерируем element с помощью <Component />, реакция может указать одни и те же элементы, потому что они генерируются одним и тем же Component
Однако HOCComponent=()=><Component />; element= <HOCComponent /> каждый раз, когда генерируется element, используется другой Component. на самом деле это HOC, построенный динамически. Поскольку HOC создается динамически внутри функции рендеринга, на первый взгляд это может сбить с толку.
Я так и не нашел официального документа об изложенной выше идее. Однако приведенного ниже кода достаточно, чтобы доказать
function TestMount(props) {
useEffect(() => {
console.info("componentDidMount", props.name);
return () => {
console.info("componentWillUnount", props.name);
};
}, []);
return <h1>Test content {" " + JSON.stringify(props.name)}</h1>;
}
function Update({ click }) {
return <button onClick = {click}>Update</button>;
}
let _Component;
function cacheComponent(C) {
if (C && !_Component) {
_Component = C;
}
return _Component || null;
}
const CacheComponent2 = once(({ name }) => <TestMount name = {name} />, []);
function App() {
const [count, setCount] = useState(0);
// can be used as a HOC of TestMount or a plain function returnnung a react element
const Component = name => <TestMount name = {name} />;
const CacheComponent1 = cacheComponent(Component);
const CacheComponent3 = useCallback(
({ name }) => <TestMount name = {name} />,
[]
);
return (
<div className = "App">
<h1>{count}</h1>
{/* used as HOC */}
<Component name = "one" />
{/* used as function returnning the element */}
{Component("two")}
<CacheComponent1 name = "three" />
<CacheComponent2 name = "four" />
<CacheComponent3 name = "five" />
<Update click = {() => setCount(x => x + 1)} />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Также приведенный выше код предоставляет три различных способа избежать нежелательного монтирования/размонтирования. Все решения каким-то образом кэшируют личность HOC
Потому что
{name: 'one'} !== {name: 'one'}, но'two' === 'two'