ReactフックuseState()とオブジェクト


114

React with Hooksで、状態を更新する正しい方法は何ですか?ネストされたオブジェクトですか?

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

(フィールドの追加)setExampleStateに更新exampleStateするには、どのように使用しaますか?

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:真})}
オマルbakhsh

回答:


122

このように新しい値を渡すことができます

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

2
素晴らしい、それはパートaに答えます!パートbのベストプラクティスを知っていますか?
isaacsultan

1
また、同じだけの過去を変えるmasterField代わりにmasterField2 setExampleState({...exampleState, masterField:{//新しい値}
aseferov

3
スプレッド演算子を使用するときは、浅いコピーに注意してください。例えば参照:stackoverflow.com/questions/43638938/...
ジョアン・マルコス・グリ

8
Dispatcherで同じ状態を使用している場合は、関数を使用する必要があります。setExampleState( exampleState => ({...exampleState, masterField2: {...etc} }) );
マイケル・ハーレー

59

誰かが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が処理します。
アンドリュー

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の回答は、オブジェクト構造全体を再入力することが目的の場合に非常にうまく機能します。ただし、ターゲット/目標がオブジェクトの特定のフィールド値を更新することである場合は、以下のアプローチの方が優れていると思います。

状況:

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
質問があります。関数を括弧で囲む必要があるのはなぜですか?内部で何が起こっているのかわかりません。あなたはたまたまそれについてもっと知りたいですか、それともどこでもっと読むことができますか?
キセノン

2
@MichaelRamageなぜ関数を括弧で囲むのですか::()これに簡単に答えるために; これsetInfoData は、本質的に高次であるためです。つまり、フックによって提供されるパワーのおかげで、引数として別の関数をとることができます:useState。この記事が高階関数をより明確にすることを願っています:sitepoint.com/higher-order-functions-javascript
OlamigokePhilip20年

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よりも適しています。

useReducerReactドキュメント

私はすでに自分の使用のためにそのようなフックを実装しました:

/**
 * 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.