Ошибка с оператором использования спреда useState / useEffect - PullRequest
2 голосов
/ 07 октября 2019

У меня проблема с установкой состояния с помощью оператора распространения, но вызов функции в useEffect только когда устанавливает последнее значение, полученное из API, в состояние.

// get all events to display on load
const getEvents = () => {
    console.log(props.id);
    axios.get(API.EVENTS.ROOT + props.id).then(
        res => {
            const data = res.data;
            setEvent(data);
            console.log(data);
            // event start and end dates
            setStart(new Date(data.start));
            setEnd(new Date(data.finish));
            // breakout dates
            for (let i = 0; i < data.breakouts.length; i++) {
                console.log(data.breakouts[i].start);
                data.breakouts[i].start = new Date(data.breakouts[i].start);
                data.breakouts[i].end = new Date(data.breakouts[i].end);
                console.log(data.breakouts[i].name, "test" + data.breakouts[i].start);
                // set date picker initial value 
                setFields({
                    ...fields,
                    [data.breakouts[i].name]: data.breakouts[i].start
                })
                console.log(fields);
            }

            setBreakoutRow(data.breakouts);
            console.log(breakoutRow);

        }
    ).catch(err => {
        console.log(err);
    })
}
// get events on load
useEffect(() => {
    console.log(props);
    getEvents();
}, []);

Я добавляю переменную состояния полей или функцию getEvents в качестве зависимости, которая обновляет state полей для всех значений, но это приводит к бесконечному циклу смерти. Это дает мне пустые даты в отображенных строках для всех, кроме последнего.

Еще одним примечанием является то, что после загрузки страницы объект полей можно без проблем обновить с помощью вызова функции, например:

const handleDateChange = (dateName, dateValue) => {
        console.log(dateName, dateValue);
        setFields({
            ...fields,
            [dateName]: dateValue
        })
        console.log(fields);
    }

Это наводит меня на мысль, что что-то не такс моим useEffect крючком, но я не уверен, где он идет не так.

Ответы [ 2 ]

3 голосов
/ 07 октября 2019

Причина в том, что вы получаете доступ к предыдущему значению для добавления нового элемента:

for (let i = 0; i < data.breakouts.length; i++) {
  data.breakouts[i].start = new Date(data.breakouts[i].start);
  setFields({
    ...fields,
    [data.breakouts[i].name]: data.breakouts[i].start
  })
}

, поскольку fields будет обновляться только при следующем рендере, вы добавляете один элемент (но каждый цикл это отдельный элемент) для массива.

То же самое было бы без зацепок / реакции:

const start = [];
for(let i = 0; i< 5; i++) console.log([...start, i]);

Это никогда не выдаст [0,1,2,3,4].

Чтовы можете сделать.

Вариант 1. Сбор данных в промежуточную переменную:

const temp = {};
for (let i = 0; i < data.breakouts.length; i++) {
  data.breakouts[i].start = new Date(data.breakouts[i].start);
  data.breakouts[i].end = new Date(data.breakouts[i].end);
  temp[data.breakouts[i].name] = data.breakouts[i].start
}
setFields({
  ...fields,
  ...temp
});

Вариант 2. Использование функциональной версии сеттера в качестве аккумулятора:

for (let i = 0; i < data.breakouts.length; i++) {
  data.breakouts[i].start = new Date(data.breakouts[i].start);
  setFields(({fields: prevFields}) => ({
    ...prevFields,
    [data.breakouts[i].name]: data.breakouts[i].start
  }))
}

IЯ бы предпочел первый вариант по двум причинам:

  1. было бы лучше, наконец, переместить преобразование данных ближе к точке вызова, поэтому вместо axios.get будет какая-то API.getMeaningfullThings возвращаемая структура, которая вам нужна;поэтому проще выполнить рефакторинг
  2. может быть, немного меньше кода лучше показывает, что происходит (это просто преобразование, а не фильтрация или что-то специфичное для React)
2 голосов
/ 07 октября 2019

Если я правильно понимаю, у вас проблема с setState из первого примера. Причина в том, что вы вызываете setState внутри цикла, и вы пытаетесь распространить старое состояние, но состояние не устанавливается до завершения цикла, поэтому вы должны накапливать все, что хотите поместить вваше состояние внутри цикла и установите его в конце.

...