В настоящее время я играю с новой функцией React Hooks и столкнулся с проблемой, когда состояние функционального компонента не обновляется, хотя я считаю, что все делаю правильно, другая пара глаз на это тестовое приложение будет очень признателен.
import React, { useState, useEffect } from 'react';
const TodoList = () => {
let updatedList = useTodoListState({
title: "task3", completed: false
});
const renderList = () => {
return (
<div>
{
updatedList.map((item) => {
<React.Fragment key = {item.title}>
<p>{item.title}</p>
</React.Fragment>
})
}
</div>
)
}
return renderList();
}
function useTodoListState(state) {
const [list, updateList] = useState([
{ title: "task1", completed: false },
{ title: "task2", completed: false }
]);
useEffect(() => {
updateList([...list, state]);
})
return list;
}
export default TodoList;
updateList в функции useEffect useTodoListState должен обновлять переменную списка, чтобы она содержала три фрагмента данных, однако это не так.





У вас есть несколько проблем:
renderList вы неправильно используете React.Fragment. Его следует использовать для обертывания нескольких узлов DOM, чтобы компонент возвращал только один узел. Вы оборачиваете отдельные элементы абзаца каждый в свой собственный фрагмент.map. Это означает, что вам нужно использовать ключевое слово return. (Подробнее о синтаксисе стрелочных функций см. в статье этот вопрос.)useEffect обновляет состояние своего собственного хука. Чтобы исправить это, вам нужно включить массив в качестве второго аргумента в useEffect, который скажет React использовать этот эффект только в том случае, если данный массив изменится.Вот как это все должно выглядеть (с небольшим переформатированием):
import React, { useState, useEffect } from 'react';
const TodoList = () => {
let updatedList = useTodoListState({
title: "task3", completed: false
});
return (
<React.Fragment> // #1: use React.Fragment to wrap multiple nodes
{
updatedList.map((item) => {
return <p key = {item.title}>{item.title}</p> // #2: return keyword inside map with braces
})
}
</React.Fragment>
)
}
function useTodoListState(state) {
const [list, updateList] = useState([
{ title: "task1", completed: false },
{ title: "task2", completed: false }
]);
useEffect(() => {
updateList([...list, state]);
}, [...list]) // #3: include a second argument to limit the effect
return list;
}
export default TodoList;
Обновлено:
Хотя я проверил, что приведенный выше код работает, в конечном итоге было бы лучше переработать структуру, чтобы удалить updateList из useEffect или внедрить другую переменную для управления обновлением.
@lecstor Да. Второй аргумент должен содержать любые значения из внешней области видимости, которые используются внутри useEffect. Здесь массив списка расширяется, поэтому массив списка в ограничителе эффекта также должен быть расширен.
хм.. Более того, useEffect будет работать только в том случае, если какие-либо значения в этом массиве изменились (кроме первого раза), поэтому я не вижу, какая разница. Если, конечно, вы не изменили список, изменив один из его элементов без изменения фактического объекта списка, что не то, что updateList сделал бы.
Я согласен с этим, но когда я тестировал это, обновление выполнялось бесконечно без распространения массива.
хм.. понял Warning: The final argument passed to useEffect changed size between renders. The order and size of this array must remain constant.codeandbox.io/s/9lw4wo1jqr
и, да, это убило мои коды и ящик без распространения, но это потому, что list обновляется useEffect, поэтому он продолжает срабатывать. Просто природа тестового приложения неверна.
@ayaz-uddin, я бы сказал, что для текущей реализации пустой массив следует использовать в качестве второго аргумента для useEffect. (для другой реализации см. codeandbox.io/s/wow5q2k9kk)
В конечном счете, было бы лучше либо реализовать другую переменную для управления обновлением, либо переработать структуру, чтобы полностью удалить обновление из useEffect.
будет ли использование
[...list]в качестве второго аргумента дляuseEffectвести себя иначе, чем просто использование[list]?