#javascript #reactjs
Вопрос:
Мне трудно понять, почему, когда внутри вектора есть объекты, он не создает новых ссылок на них при копировании вектора.
Но вот в чем проблема.
const USERS_TYPE = [
{name:"User",icon:faTruck,
inputs:[
{label:"Name",required:true,width:"200px",value:"", error:false},
{label:"ID",required:true,width:"150px",value:"", error:false},
{label:"Email",required:false,width:"150px",value:"", error:false},
]}]
Я попытался передать этот вектор в состояние двумя способами.
const [users,setUsers] = useState(USERS_TYPE.map(item=>({...item})))
const [users,setUsers] = useState([...USERS_TYPE])
И в обеих ситуациях изменение пользователя с помощью setUser приведет к изменению типа ПОЛЬЗОВАТЕЛЯ.
Один из способов, которым я меняюсь.
const changes = [...users]
const err = validation(changes[selected-1].inputs)
err.map((index)=>{
changes[selected-1].inputs[index].error = true
})
setUsers(changes)
Какие решения я мог бы придумать, перейти от вектора к объекту, другой механизм копирования.
Эта копия не имеет особого смысла, поскольку внутренние ссылки на объекты остаются нетронутыми.
Изменить: Еще одна важная деталь заключается в том, что тип пользователя находится вне компонента функции.
Ответ №1:
это не создает новых ссылок на них при копировании вектора
Потому что JS работает не так. Он не делает глубокие клонирования автоматически.
const user1 = {id:1}
const user2 = {id:2}
const users = [user1,user2];
const newUsers = [...users]; // this clones users, BUT NOT the objects it contains
console.log(newUsers === users); // false, it's a new array
console.log(newUsers[0] === users[0]) // true, it's the same reference
В конечном счете, вы просто мутируете состояние. Первое и самое золотое правило реакции: не изменяйте состояние.
Это строка, вызывающая ошибку:
err.map((index)=>{
// you are mutating an object by doing this
// yes, `changes` is a new array, but you are still mutating the object that is part of state that is nested inside of that array
changes[selected-1].inputs[index].error = true
})
Может быть, это сработает:
const idx = selected-1;
const err = validation(users[idx].inputs)
setUsers(users => users.map((user,i) => {
if(i !== idx) return user; // you aren't interested in this user, leave it unmodified
// you need to change the inputs for this user
// first, shallow clone the object using the spread operator
return {
...user,
// now inputs must be a new reference as well, so clone it using map
inputs: user.inputs.map((input,index) => ({
// careful not to mutate the input object, clone it using spread
...input,
// and set the error property on the cloned object
error: !!err[index]
}))
}
}))
ПРАВКА: Извините за все правки кода, у меня была куча синтаксических ошибок. Конечная точка, которую я пытался донести, оставалась неизменной.
ПРАВКА №2:
Еще одна важная деталь заключается в том, что тип пользователя находится вне компонента функции.
В данном случае это на самом деле не имеет значения, поскольку служит вашим начальным состоянием. Каждый раз, когда вы обновляете состояние, вам нужно делать это неизменно (как я показал вам выше), чтобы не изменять этот глобальный объект. Если вы действительно измените его, вы увидите, что размонтирование компонента и повторная установка компонента приведут к тому, что будет выглядеть как «сохраненное состояние», но это просто то, что вы изменили глобальный объект, который служил шаблоном для начального состояния.