Лучшие способы обновить объект массива состояний с помощью перехватчиков реакции?

#reactjs #react-hooks

#reactjs #react-перехваты

Вопрос:

На самом деле у меня есть список элементов, которые должны храниться в массиве как состояние реакции.

 const [data, setData] = useState([{"name": "joy", "age": 25}, {"name": "tom", "age": 41}]);
  

Каким-то образом мне нужно обновить только определенный объект, может быть имя или возраст.

Я делаю таким образом. Но это кажется не очень хорошим.

     setData(prevState => {
      let obj = prevState.find(o => anycondition);
      if(obj !== undefined) {
        obj.name = "Demo";
      }
      return [...prevState];
    })
  

есть ли какой-либо другой способ обновить только объект из массива состояний с помощью реактивных перехватов?

Ответ №1:

Это изменение состояния!!

 setData(prevState => {
  let obj = prevState.find(o => anycondition);
  if(obj !== undefined) {
    obj.name = "Demo"; // <-- state mutation
  }
  return [...prevState];
})
  

Всегда следует неглубоко копировать текущее состояние, которое обновляется

 setData(prevState => {
  return prevState.map(el => <condition> ? { // <-- map state to new array
    ...el,        // <-- copy element
    name: "Demo", // <-- write new property
  } : el);
})
  

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

 setData(prevState => {
  return prevState.map(el => { // <-- map state to new array
    // any mapping logic
    ...

    if (<condition>) {
      // any other logic
      ...
      return {
        ...el,        // <-- copy element
        name: "Demo", // <-- write new property
      }
    } else {
      return el;
    }
  });
})
  

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

1. Как выполнить какие-либо вычисления при отображении prevState?

2. @user10328862 Любое вычисление может быть выполнено в обратном вызове map. Что вы пытаетесь вычислить?

3. Можете ли вы упростить свой код ответа, добавив условие if else?

4. @user10328862 Ты имеешь в виду сравнение с троичным использованием?

5. @user10328862 обновил ответ с возможным примером использования if-else.

Ответ №2:

Может быть, вы хотите что-то вроде этого?

здесь вы будете выполнять цикл только один раз и обновлять данные, которые вы хотите

 changeHandler = event => {
    const [name, value] = event.target.name
    setData(prevState => prevState.map(item => {
        if (anycondition) {
            return {
                ...item,
                [name]: value
            }
        }
        return item
    }))
}
  

Если вы также хотите получить некоторый идентификатор из входных данных, вы можете передать его с наборами данных, как здесь data-id={1}

А затем получить его из события, подобного этому, const id = e.target.dataset.id; в changeHandler функции

         <input
          type="text"
          name="name"
          data-id={1}
          value={this.state.data.name.fname}
          onChange={this.changeHandler}
        />
  

Ответ №3:

Чтобы справиться с этим с большим контролем, вы можете добавить идентификатор prop к каждому объекту.

Если вы хотите изменить конкретный объект, просто сделайте:

 const [data, setData] = useState([
    {   
        id: 1,
        name: "joy",
        age: 25
    },
    {
        id: 2,
        name: "tom", 
        age: 41
    }
]);


const updateItem = (id, attributes) => {
  const index = data.findIndex(item => item.id === id);

  if (index === -1)
    return;
  
  setData(
      [
         ...data.slice(0, index),
         Object.assign({}, data[index], attributes),
         ...data.slice(index   1)
      ]
  );
}


// You can update the second item as follows
updateItem(2, { age: 50 })
  

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

1. Кроме того, вы можете продолжить без идентификатора, но вам придется искать элемент с вашим собственным условием.

2. Хотя этот код все еще выполняет поиск по условию (соответствует id ), и выполняет еще несколько проходов по данным, чем это действительно необходимо. 1 проход для поиска индекса, 2 прохода для копирования до индекса и среза затем распространения в новый массив, 2 прохода для копирования после индекса и среза затем распространения в новый массив.

3. @VictorMolina Вы путаете this.state с useState и, вероятно, лучше использовать Array.prototype.map для обновления элемента массива

Ответ №4:

попробуйте этот способ 😉

 function mergeState(state, payload) {
  return { ...state, ...payload }
}

const [state, dispatch] = useReducer(mergeState, {
 name: '',
 age: 0,
 data: [{"name": "joy", "age": 25}, {"name": "tom", "age": 41}],
})

// get the data
const data = state.data.map(item => {
  if(obj !== undefined){
     obj.name = "Demo";
  }
  return item
})

// update
dispatch({data})