web-dev-qa-db-ja.com

ReactオブジェクトとuseState()をフックします

状態を更新する正しい方法は何ですか?React with Hooks?)

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

setExampleStateをどのように使用してexampleStateaに更新しますか(フィールドの追加)?

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"
           }
        }
   })

51
isaacsultan

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

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

一般に、React状態で深くネストされたオブジェクトに注意する必要があります。予期しない動作を回避するために、状態は不変に更新する必要があります。深いオブジェクトがある場合、不変性のためにそれらを深く複製することになります。 Reactではかなり高価になる可能性があります。

状態をディープクローンすると、Reactは、変数が変更されていなくても、変数に依存するすべてを再計算して再レンダリングします。

したがって、問題を解決する前に、まず状態を平坦化する方法を考えてください。それを行うとすぐに、useReducer()などの大きな状態の処理に役立つ美しいツールが見つかります。

考えたが、深くネストされた状態ツリーを使用する必要があると確信している場合は、useState()を immutable.jsImmutability-helper 。可変性を気にすることなく、ディープオブジェクトを簡単に更新または複製できます。

21
Ilyas Assainov

オブジェクトを不変に更新するユーティリティ関数を残します

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

それは私が思いついたES6ソリューションで、状態にネストされたオブジェクトを更新できます。

export const updateStateObject = (object, keyToUpdate, newValue) => {

  let updatedObject = {};

  // Iterate over the object
  Object.entries(object).forEach(([key, value]) => {

    // Update the chosen key
    if (key === keyToUpdate) Object.assign(updatedObject, { [key]: newValue });

    // If the iterated key doesn't match the given key return the current value
    else if (key !== keyToUpdate) Object.assign(updatedObject, { [key]: value });
  });

  return updatedObject;
};

次に、状態を更新する必要がある場合は、次を呼び出します。

setState({
  ...state,
  key: updateStateObject(object, keyToUpdate, newValue)
})
0
Gabriele Rapone

私はパーティーに遅れました.. :)

オブジェクト構造全体を再入力することが目的の場合、@ 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"

    }));

これが同様の問題を解決しようとしている人を助けることを願っています。

0
Eman Yalpsid