React Hooks useState () dengan Object

103

Apa cara yang benar untuk memperbarui status, apakah objek bersarang, di React with Hooks?

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

Bagaimana cara yang digunakan setExampleStateuntuk memperbarui exampleStateke a(menambahkan bidang)?

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

b (Mengubah nilai)?

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

isaacsultan
sumber
maksud Anda menambahkan nilai kunci objek baru ke objek yang ada?
Cukup kode
@Justcode Untuk contoh pertama ya, untuk contoh kedua hanya mengubah objek yang ada
isaacsultan

Jawaban:

109

Anda bisa memberikan nilai baru seperti ini

  setExampleState({...exampleState,  masterField2: {
        fieldOne: "c",
        fieldTwo: {
           fieldTwoOne: "d",
           fieldTwoTwo: "e"
           }
        },
   }})
aseferov
sumber
2
Hebat, itu jawaban bagian a! apakah Anda tahu praktik terbaik untuk bagian b?
isaacsultan
1
mengubah juga masa lalu yang sama masterFieldalih-alih masterField2 setExampleState({...exampleState, masterField:{// nilai baru}
aseferov
3
Waspadai penyalinan dangkal saat menggunakan operator penyebaran. Lihat misalnya: stackoverflow.com/questions/43638938/…
João Marcos Gris
7
Jika Anda menggunakan status yang sama dengan Dispatcher-nya, Anda harus menggunakan sebuah fungsi. setExampleState( exampleState => ({...exampleState, masterField2: {...etc} }) );
Michael Harley
41

Umumnya Anda harus berhati-hati terhadap objek bersarang dalam dalam status React. Untuk menghindari perilaku yang tidak terduga, status harus selalu diperbarui. Saat Anda memiliki objek dalam, Anda akhirnya akan melakukan kloning mendalam untuk mengetahui kekekalan, yang bisa sangat mahal di React. Mengapa?

Setelah Anda melakukan deep clone state, React akan menghitung ulang dan merender ulang semua yang bergantung pada variabel, meskipun variabel tersebut tidak berubah!

Jadi, sebelum mencoba menyelesaikan masalah Anda, pikirkan bagaimana Anda dapat meratakan keadaan terlebih dahulu. Segera setelah Anda melakukannya, Anda akan menemukan alat cantik yang akan membantu menangani status besar, seperti useReducer ().

Jika Anda memikirkannya, tetapi masih yakin bahwa Anda perlu menggunakan pohon status bersarang yang dalam, Anda masih bisa menggunakan useState () dengan pustaka seperti immutable.js dan Immutability-helper . Mereka membuatnya mudah untuk memperbarui atau mengkloning objek yang dalam tanpa harus khawatir tentang mutabilitas.

Ilyas Assainov
sumber
Anda juga dapat menggunakan Hookstate (penafian: Saya seorang penulis) untuk mengelola data status kompleks (lokal dan global) tanpa kloning yang dalam kemudian dan tanpa mengkhawatirkan pembaruan yang tidak perlu - Hookstate akan menanganinya untuk Anda.
Andrew
41

Jika ada yang mencari update hook useState () untuk objek

- 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'
         }));
Maheshvirus
sumber
3
Persis apa yang saya cari. Hebat!
StackedActor
7

Terima kasih Philip, ini membantu saya - kasus penggunaan saya adalah saya memiliki formulir dengan banyak bidang input jadi saya mempertahankan status awal sebagai objek dan saya tidak dapat memperbarui status objek. Posting di atas membantu saya :)

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} 
/>


Kavitha Vikas
sumber
6

Saya terlambat ke pesta .. :)

Jawaban @aseferov bekerja dengan sangat baik ketika tujuannya adalah untuk memasukkan kembali seluruh struktur objek. Namun, jika target / sasaran adalah memperbarui nilai bidang tertentu di Objek, saya yakin pendekatan di bawah ini lebih baik.

situasi:

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

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

    }

  });

Memperbarui catatan tertentu akan membutuhkan penarikan kembali ke Status sebelumnya prevState

Sini:

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

mungkin

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

    }));

Saya harap ini membantu siapa pun yang mencoba memecahkan masalah serupa.

Olamigoke Philip
sumber
Saya punya pertanyaan. Mengapa kita perlu membungkus fungsi dalam tanda kurung? Saya tidak yakin apa yang terjadi di bawah tenda. Apakah Anda tahu atau di mana saya dapat membaca lebih lanjut tentang itu?
Xenon
1
@MichaelRamage Mengapa kita membungkus fungsi dalam tanda kurung ():: Untuk menjawab ini secara sederhana; itu karena setInfoData sifatnya yang tinggi, yaitu dapat mengambil fungsi lain sebagai argumen, berkat kekuatan yang disediakan oleh Hook: useState. Saya harap artikel ini akan memberikan kejelasan lebih lanjut tentang fungsi tingkat tinggi: sitepoint.com/higher-order-functions-javascript
Olamigoke Philip
1
Sangat membantu, terutama untuk kasus di mana Anda mencoba memperbarui properti yang bersarang beberapa lapisan, yaitu: first.second.third - contoh lain mencakup first.second, tetapi tidak first.second.third
Craig Presti - 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>
  );
}
vikas kumar
sumber
1

Saya pikir solusi terbaik adalah Immer . Ini memungkinkan Anda untuk memperbarui objek seperti Anda mengubah bidang secara langsung (masterField.fieldOne.fieldx = 'abc'). Tapi itu tidak akan mengubah objek yang sebenarnya tentunya. Ini mengumpulkan semua pembaruan pada objek draf dan memberi Anda objek akhir di bagian akhir yang dapat Anda gunakan untuk menggantikan objek asli.

Deniz
sumber
0

Saya meninggalkan Anda fungsi utilitas untuk memperbarui objek secara pasti

/**
 * 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
  };
};

Jadi Anda bisa menggunakannya seperti ini

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
}

Jika tidak, Anda memiliki lebih banyak utilitas di sini: https://es.reactjs.org/docs/update.html

Gonzalo
sumber
0

Awalnya saya menggunakan objek di useState, tetapi kemudian saya pindah ke hook useReducer untuk kasus yang kompleks. Saya merasakan peningkatan kinerja ketika saya mengubah kode.

useReducer biasanya lebih disukai daripada useState jika Anda memiliki logika status kompleks yang melibatkan beberapa sub-nilai atau jika status berikutnya bergantung pada yang sebelumnya.

useReducer React docs

Saya sudah menerapkan pengait seperti itu untuk penggunaan saya sendiri:

/**
 * 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);
      }
    },
  ];
}

lebih banyak kait terkait .

eyalyoli
sumber