Перехваты и неизменяемое состояние

#reactjs #react-hooks

#reactjs #реагирующие хуки

Вопрос:

Заранее приношу извинения, если на эти вопросы уже были даны ответы ранее (а также за длинный пост, но я старался быть максимально конкретным). Но ответы, которые я нашел, меня не полностью удовлетворяют.

Я хочу использовать новые удивительные хуки React для своего проекта. И то, что я делал до сих пор, было прямым.

Теперь я столкнулся с более сложным примером, и я не уверен, как мне лучше всего справиться с этим.

Допустим, у меня более сложный объект (по крайней мере, он не плоский) в моем состоянии.

 {
    persons: [
        {
            id: 1,
            name: 'Joe Doe',
            age: 35,
            country: 'Spain',
            interests: [
                { id: 1, en: 'Football', es: 'Fútbol' },
                { id: 2, en: 'Travelling', es: 'Viajar' }
            ]
        },
        {
            id: 2,
            name: 'Foo Bar',
            age: 28,
            country: 'Spain',
            interests: [
                { id: 3, en: 'Computers', es: 'Computadoras' },
                { id: 4, en: 'Cooking', es: 'Cocinar' }
            ]
        }
    ],
    amount: 2,
    foo: 'bar'
}
  

Каков наилучший способ:

  1. Добавьте элемент (объект) в массив моих коллег
  2. Добавить элемент в определенный массив «интересов»?
  3. Манипулировать значением свойства внутри объекта в массиве?
  4. Изменить значение вне массива persons, например foo ?

используя перехват useState?

Следующие примеры кода попытаются проиллюстрировать каждый вопрос. Они не тестировались…

Давайте рассмотрим, что у меня есть контейнер, который начинается с этого. Он также включает в себя функции, которые разделены в остальной части поста.

 const [colleagues, setColleagues] = useState({});

// using the useEffect as "componentDidMount
useEffect(() => {

    // receiving data from ajax request
    // and set the state as the example object provided earlier
    setColleagues(response.data);
}, []);
  

1) Итак, что касается первого вопроса. Это допустимо?
Или мне нужно убедиться, что каждый объект в моем persons array разрушен?

 const onAddNewColleague = () => {

    // new colleague, this data would be dynamic
    // but for the sake of this example I'll just hard code it
    const newColleague = {
        name: 'Foo Baz Bar',
        age: 30,
        country: 'Spain',
        interests: [
            { en: 'Cats', es: 'Gatos' }
        ]
    };

    // creates a copy of the state
    // targets the "persons" prop and adds a new array
    // where the new colleague is appended
    const newState = {
        ...colleagues,
        persons: colleagues.concat(newColleague)

    };

    // updates the state
    setColleagues(newState);
};
  

2) This feels wrong as I end up updating the entire persons array instead of just the interest array for a specific person.

 const onAddNewInterest = (personId) => {
    // a new interest
    const newInterest = {
        en: 'Languages', es: 'Idiomas'
    };

    // find the person according to personId
    // and update the interests
    const updatedPersons = colleagues.persons.map(person => {
        if(person.id === personId) {
            return {
                ...person,
                interests: person.interests.concat(newInterest);
            };
        }

        return person;
    });

    // create a copy of the state
    const newState = {
        ...colleagues,
        persons: [...updatedPersons]
    };

    setColleagues(newState);
};
  

3) В качестве второго примера этот тоже кажется неправильным, поскольку я обновляюсь полностью, persons array когда на самом деле я мог бы просто захотеть изменить age одного конкретного человека

 const onChangeValue = (personId, key, value) => {

    // find the person
    const updatedPersons = colleagues.persons.map(person => {

        if(person.id === personId) {
            // key, could be age?
            return {
                ...person,
                [key]: value
            };
        }

        return person;
    });

    // create a copy of the state
    const newState = {
        ...colleagues,
        persons: [...updatedPersons]
    };

    setColleagues(newState);
};
  

4) Это допустимо, или мне нужно уничтожить каждую часть моего colleagues object по отдельности?

 const onChangeOtherValue = (key, value) => {
    // for example changing the value foo

    const newState = {
        ...colleagues,
        [key]: value
    };

    setColleagues(newState);
};
  

У меня действительно есть ощущение, что только концепция первой функции допустима, в то время как остальные из них — нет.

Можно ли это сделать легко, или я должен просто использовать неизменяемый помощник?

Заранее спасибо!

Обновлены примеры, чтобы получить синтаксис, верно. Спасибо, Валерий.

Чтобы прояснить, что мне действительно нужно, вот лучшая практика для обработки вариантов использования, подобных этому. Я хочу убедиться, что мое состояние обновляется наиболее правильным и эффективным способом. Так что не стесняйтесь разбирать мои примеры на части или писать новые — я весь внимание. Нет необходимости просто модифицировать мой, чтобы он соответствовал этому сообщению (если только они на самом деле не окажутся хорошими).

Комментарии:

1. Вы рассматривали возможность использования useReducer хука? Это упростит более сложный переход состояния, поскольку вы можете предоставить более полный набор переходов и переместить логику за пределы компонента

2. Привет! Я заглянул в useReducer, и это тоже классный хук! Но мне все еще нужно убедиться, что мое состояние обновляется неизменяемым образом, верно? Именно в этом вложенном состоянии я чувствую неуверенность.. Не стесняйтесь добавлять пример 🙂

3. То, что вы хотите, вероятно, называется «структурное совместное использование» — используйте как можно больше и копируйте только то, что необходимо.

Ответ №1:

1) ОК

2)

     const updatedPersons = colleagues.persons.map(person => {
        if(person.id === personId) {
            return {
                ...person,
                interests: person.interests.concat({ en: 'Test', es: 'Test' })
            };
        }

        return person;
    });

    const newState = {
        ...colleagues,
        persons: updatedPersons
    };
  

3)

     const updatedPersons = colleagues.persons.map(person => {

        if(person.id === personId) {
            return {
                ...person,
                [key]: value
            };
        }

        return person;
    });

    // create a copy of the state
    const newState = {
        ...colleagues,
        persons: updatedPersons
    };
  

4) ОК

Комментарии:

1. Спасибо за ваш ответ. Я добавил еще один пример к своему сообщению. Подходы, которые вы предоставили, например, 2 и 3, заставляют меня обновлять весь persons array , даже если изменения применимы только к конкретному пользователю. Это правильный путь? Или есть способ получше? Поскольку другие persons не подвергаются манипуляциям, я предполагаю, что они не мутировали, и эти объекты не нужно применять как новый объект. Или я должен на самом деле return { ...person} убедиться, или это не имеет смысла?

2. @entiendoNull, Вы не обновляете массив. При использовании map создается новый массив. Важный момент в том, что вам не нужно создавать совершенно новое состояние, вам нужно создавать новые экземпляры только для объектов, которые вы собираетесь обновлять.

Ответ №2:

для первого я бы сделал так,

     const newState = {
          ...colleagues,
          persons: [...persons, newColleague]

    };