React Hooks useState () with Object


116

React with Hooks에서 상태를 업데이트하는 올바른 방법은 무엇입니까?

export Example = () => {
  const [exampleState, setExampleState] = useState(
  {masterField: {
        fieldOne: "a",
        fieldTwo: {
           fieldTwoOne: "b"
           fieldTwoTwo: "c"
           }
        }
   })

한 사람이 어떻게 사용하는 것이 setExampleState업데이 트에 exampleStatea(AN 필드를 추가)?

const a = {
masterField: {
        fieldOne: "a",
        fieldTwo: {
           fieldTwoOne: "b",
           fieldTwoTwo: "c"
           }
        },
  masterField2: {
        fieldOne: "c",
        fieldTwo: {
           fieldTwoOne: "d",
           fieldTwoTwo: "e"
           }
        },
   }
}

b (값 변경)?

const b = {masterField: {
        fieldOne: "e",
        fieldTwo: {
           fieldTwoOne: "f"
           fieldTwoTwo: "g"
           }
        }
   })


기존 개체에 새 개체 키 값을 추가한다는 의미입니까?
코드 작성

첫 번째 예를 들어 @Justcode 예, 두 번째 예를 들어 단지 기존 객체 변경
isaacsultan

onValueChange = {() => setSelection ({... prev, id_1 : true})}
Omar bakhsh

답변:


125

이렇게 새로운 값을 전달할 수 있습니다.

  setExampleState({...exampleState,  masterField2: {
        fieldOne: "c",
        fieldTwo: {
           fieldTwoOne: "d",
           fieldTwoTwo: "e"
           }
        },
   }})

2
좋습니다. 파트 a에 대한 답변입니다! 파트 b의 모범 사례를 알고 있습니까?
isaacsultan

1
{// new values masterField대신 과거에도 동일하게 변경 masterField2 setExampleState({...exampleState, masterField:}
aseferov

3
스프레드 연산자를 사용할 때 얕은 복사에주의하십시오. 예를 들면 다음을 참조하십시오. stackoverflow.com/questions/43638938/…
João Marcos Gris

9
Dispatcher와 동일한 상태를 사용하는 경우 함수를 사용해야합니다. setExampleState( exampleState => ({...exampleState, masterField2: {...etc} }) );
Michael Harley

64

누군가 useState ()를 검색하는 경우 객체에 대한 후크 업데이트

- Through Input

        const [state, setState] = useState({ fName: "", lName: "" });
        const handleChange = e => {
            const { name, value } = e.target;
            setState(prevState => ({
                ...prevState,
                [name]: value
            }));
        };

        <input
            value={state.fName}
            type="text"
            onChange={handleChange}
            name="fName"
        />
        <input
            value={state.lName}
            type="text"
            onChange={handleChange}
            name="lName"
        />
   ***************************

 - Through onSubmit or button click
    
        setState(prevState => ({
            ...prevState,
            fName: 'your updated value here'
         }));

3
내가 찾던 바로 그것. 대박!
StackedActor

42

일반적으로 React 상태에서 깊이 중첩 된 객체를주의해야합니다. 예기치 않은 동작을 방지하려면 상태를 변경없이 업데이트해야합니다. 딥 오브젝트가 있으면 불변성을 위해 딥 클로닝을하게되는데 이는 React에서 상당히 비쌀 수 있습니다. 왜?

상태를 딥 복제하면 React는 변수가 변경되지 않았더라도 변수에 의존하는 모든 것을 다시 계산하고 다시 렌더링합니다!

따라서 문제를 해결하기 전에 먼저 상태를 평평하게 할 수있는 방법을 생각하십시오. 그렇게하면 useReducer ()와 같은 큰 상태를 처리하는 데 도움이되는 멋진 도구를 찾을 수 있습니다.

그것에 대해 생각했지만 여전히 깊게 중첩 된 상태 트리를 사용해야한다고 확신하는 경우 immutable.jsImmutability-helper 와 같은 라이브러리에서 useState ()를 사용할 수 있습니다 . 변경 가능성에 대해 걱정할 필요없이 딥 오브젝트를 간단하게 업데이트하거나 복제 할 수 있습니다.


또한 Hookstate (면책 조항 : 저자입니다)를 사용하여 딥 복제없이 복잡한 (로컬 및 글로벌) 상태 데이터를 관리 할 수 ​​있으며 불필요한 업데이트에 대해 걱정할 필요가 없습니다. Hookstate가 대신 처리합니다.
Andrew

8

필립 덕분에 도움이되었습니다-제 사용 사례는 입력 필드가 많은 양식을 가지고 있었기 때문에 초기 상태를 객체로 유지하고 객체 상태를 업데이트 할 수 없었습니다. 위의 게시물이 도움이되었습니다 :)

const [projectGroupDetails, setProjectGroupDetails] = useState({
    "projectGroupId": "",
    "projectGroup": "DDD",
    "project-id": "",
    "appd-ui": "",
    "appd-node": ""    
});

const inputGroupChangeHandler = (event) => {
    setProjectGroupDetails((prevState) => ({
       ...prevState,
       [event.target.id]: event.target.value
    }));
}

<Input 
    id="projectGroupId" 
    labelText="Project Group Id" 
    value={projectGroupDetails.projectGroupId} 
    onChange={inputGroupChangeHandler} 
/>



6

파티에 늦었어요 .. :)

@aseferov 대답은 전체 개체 구조를 다시 입력하려는 경우 매우 잘 작동합니다. 그러나 목표 / 목표가 Object의 특정 필드 값을 업데이트하는 것이라면 아래 접근 방식이 더 낫다고 생각합니다.

상태:

const [infoData, setInfoData] = useState({
    major: {
      name: "John Doe",
      age: "24",
      sex: "M",
    },

    minor:{
      id: 4,
      collegeRegion: "south",

    }

  });

특정 기록을 업데이트하려면 이전 주를 리콜해야합니다. prevState

여기:

setInfoData((prevState) => ({
      ...prevState,
      major: {
        ...prevState.major,
        name: "Tan Long",
      }
    }));

혹시

setInfoData((prevState) => ({
      ...prevState,
      major: {
        ...prevState.major,
        name: "Tan Long",
      },
      minor: {
        ...prevState.minor,
        collegeRegion: "northEast"

    }));

나는 이것이 비슷한 문제를 해결하려는 모든 사람에게 도움이되기를 바랍니다.


1
질문이 있습니다. 함수를 괄호로 묶어야하는 이유는 무엇입니까? 나는 후드 아래에서 무슨 일이 일어나고 있는지 잘 모르겠습니다. 당신은 알고 있거나 내가 그것에 대해 더 읽을 수있는 곳이 있습니까?
Xenon

2
@MichaelRamage 함수를 괄호로 감싸는 이유는 다음과 ()같습니다. 그것은 setInfoData 본질적으로 높은 질서 이기 때문 입니다. 즉, Hook가 제공하는 힘의 호의에 따라 다른 함수를 인수로 취할 수 있습니다 : useState. 나는이 문서가 고차 함수에 대한 자세한 설명을 제공하기 바랍니다 : sitepoint.com/higher-order-functions-javascript
Olamigoke 필립

2
매우 도움이, 특히 여러 층 깊이, 즉 중첩 된 속성 업데이트하려고하는 경우에 : first.second.third - 다른 예는 first.second을 커버하지만 first.second.third
크레이그 프레스 티를 - MSFT

3
function App() {

  const [todos, setTodos] = useState([
    { id: 1, title: "Selectus aut autem", completed: false },
    { id: 2, title: "Luis ut nam facilis et officia qui", completed: false },
    { id: 3, title: "Fugiat veniam minus", completed: false },
    { id: 4, title: "Aet porro tempora", completed: true },
    { id: 5, title: "Laboriosam mollitia et enim quasi", completed: false }
  ]);

  const changeInput = (e) => {todos.map(items => items.id === parseInt(e.target.value) && (items.completed = e.target.checked));
 setTodos([...todos], todos);}
  return (
    <div className="container">
      {todos.map(items => {
        return (
          <div key={items.id}>
            <label>
<input type="checkbox" 
onChange={changeInput} 
value={items.id} 
checked={items.completed} />&nbsp; {items.title}</label>
          </div>
        )
      })}
    </div>
  );
}

1

최고의 솔루션은 Immer 라고 생각 합니다. 필드를 직접 수정하는 것처럼 개체를 업데이트 할 수 있습니다 (masterField.fieldOne.fieldx = 'abc'). 그러나 물론 실제 대상을 변경하지는 않습니다. 초안 개체의 모든 업데이트를 수집하고 마지막에 원본 개체를 대체하는 데 사용할 수있는 최종 개체를 제공합니다.


0

나는 당신에게 객체를 불변으로 업데이트하는 유틸리티 함수를 남깁니다.

/**
 * Inmutable update object
 * @param  {Object} oldObject     Object to update
 * @param  {Object} updatedValues Object with new values
 * @return {Object}               New Object with updated values
 */
export const updateObject = (oldObject, updatedValues) => {
  return {
    ...oldObject,
    ...updatedValues
  };
};

그래서 이렇게 사용할 수 있습니다

const MyComponent = props => {

  const [orderForm, setOrderForm] = useState({
    specialities: {
      elementType: "select",
      elementConfig: {
        options: [],
        label: "Specialities"
      },
      touched: false
    }
  });


// I want to update the options list, to fill a select element

  // ---------- Update with fetched elements ---------- //

  const updateSpecialitiesData = data => {
    // Inmutably update elementConfig object. i.e label field is not modified
    const updatedOptions = updateObject(
      orderForm[formElementKey]["elementConfig"],
      {
        options: data
      }
    );
    // Inmutably update the relevant element.
    const updatedFormElement = updateObject(orderForm[formElementKey], {
      touched: true,
      elementConfig: updatedOptions
    });
    // Inmutably update the relevant element in the state.
    const orderFormUpdated = updateObject(orderForm, {
      [formElementKey]: updatedFormElement
    });
    setOrderForm(orderFormUpdated);
  };

  useEffect(() => {
      // some code to fetch data
      updateSpecialitiesData.current("specialities",fetchedData);
  }, [updateSpecialitiesData]);

// More component code
}

그렇지 않은 경우 여기에 더 많은 유틸리티가 있습니다 : https://es.reactjs.org/docs/update.html


0

처음에는 useState에서 객체를 사용했지만 복잡한 경우에는 useReducer 후크로 이동했습니다. 코드를 리팩토링 할 때 성능 향상을 느꼈습니다.

useReducer는 일반적으로 여러 하위 값을 포함하는 복잡한 상태 논리가 있거나 다음 상태가 이전 상태에 종속 될 때 useState보다 선호됩니다.

useReducer React 문서

나는 이미 내 자신의 용도로 이러한 후크를 구현했습니다.

/**
 * Same as useObjectState but uses useReducer instead of useState
 *  (better performance for complex cases)
 * @param {*} PropsWithDefaultValues object with all needed props 
 * and their initial value
 * @returns [state, setProp] state - the state object, setProp - dispatch 
 * changes one (given prop name & prop value) or multiple props (given an 
 * object { prop: value, ...}) in object state
 */
export function useObjectReducer(PropsWithDefaultValues) {
  const [state, dispatch] = useReducer(reducer, PropsWithDefaultValues);

  //newFieldsVal={[field_name]: [field_value], ...}
  function reducer(state, newFieldsVal) {
    return { ...state, ...newFieldsVal };
  }

  return [
    state,
    (newFieldsVal, newVal) => {
      if (typeof newVal !== "undefined") {
        const tmp = {};
        tmp[newFieldsVal] = newVal;
        dispatch(tmp);
      } else {
        dispatch(newFieldsVal);
      }
    },
  ];
}

더 많은 관련 후크 .


0

, 다음 예와 같이 수행하십시오.

객체의 첫 번째 생성 상태 :

const [isSelected, setSelection] = useState({ id_1: false }, { id_2: false }, { id_3: false });

그런 다음 값을 변경하십시오.

// if the id_1 is false make it true or return it false.

onValueChange={() => isSelected.id_1 == false ? setSelection({ ...isSelected, id_1: true }) : setSelection({ ...isSelected, id_1: false })}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.