Updating Objects in State
Treat state as read-only
import { useState } from "react"; function App() { const [plan, setPlan] = useState({ x: 1, y: 2 }); return ( <> {plan.x} <button onClick={() => (plan.x += 1)}>a</button> </> ); } export default App;
上記のコードでボタンを押下しても,画面に表示されるplan.x
の値は変化しない。それは,state の setter 関数を使用しないと,React は object が変更されたことがわからないため。
state の setter 関数により,以下のことを React に伝える。
Using a single event handler for multiple fields
import { ChangeEvent, useState } from "react"; function App() { const [person, setPerson] = useState({ firstName: "Barbara", lastName: "Hepworth", email: "bhepworth@sculpture.com", }); function handleChange(e: ChangeEvent<HTMLInputElement>) { setPerson({ ...person, [e.target.name]: e.target.value, }); } return ( <> <label> First name: <input name="firstName" value={person.firstName} onChange={handleChange} /> </label> <label> Last name: <input name="lastName" value={person.lastName} onChange={handleChange} /> </label> <label> Email: <input name="email" value={person.email} onChange={handleChange} /> </label> <p> {person.firstName} {person.lastName} ({person.email}) </p> </> ); } export default App;
[e.target.name]: e.target.value
とすることで,動的に key を指定できる。
Updating a nested object
import { ChangeEvent, useState } from "react"; type Event = ChangeEvent<HTMLInputElement>; function App() { const [person, setPerson] = useState({ name: "Niki de Saint Phalle", artwork: { title: "Blue Nana", city: "Hamburg", image: "https://i.imgur.com/Sd1AgUOm.jpg", }, }); function handleNameChange(e: Event) { setPerson({ ...person, name: e.target.value, }); } function handleTitleChange(e: Event) { setPerson({ ...person, artwork: { ...person.artwork, title: e.target.value, }, }); } function handleCityChange(e: Event) { setPerson({ ...person, artwork: { ...person.artwork, city: e.target.value, }, }); } function handleImageChange(e: Event) { setPerson({ ...person, artwork: { ...person.artwork, image: e.target.value, }, }); } return ( <> <label> Name: <input value={person.name} onChange={handleNameChange} /> </label> <label> Title: <input value={person.artwork.title} onChange={handleTitleChange} /> </label> <label> City: <input value={person.artwork.city} onChange={handleCityChange} /> </label> <label> Image: <input value={person.artwork.image} onChange={handleImageChange} /> </label> <p> <i>{person.artwork.title}</i> {" by "} {person.name} <br /> (located in {person.artwork.city}) </p> <img src={person.artwork.image} alt={person.artwork.title} /> </> ); } export default App;
nest した object でも,コピーを使用して値を更新する必要がある。
Write concise update logic with Immer
import { ChangeEvent } from "react"; import { useImmer } from "use-immer"; type Event = ChangeEvent<HTMLInputElement>; function App() { const [person, updatePerson] = useImmer({ name: "Niki de Saint Phalle", artwork: { title: "Blue Nana", city: "Hamburg", image: "https://i.imgur.com/Sd1AgUOm.jpg", }, }); function handleNameChange(e: Event) { updatePerson((draft) => { draft.name = e.target.value; }); } function handleTitleChange(e: Event) { updatePerson((draft) => { draft.artwork.title = e.target.value; }); } function handleCityChange(e: Event) { updatePerson((draft) => { draft.artwork.city = e.target.value; }); } function handleImageChange(e: Event) { updatePerson((draft) => { draft.artwork.image = e.target.value; }); } return ( <> <label> Name: <input value={person.name} onChange={handleNameChange} /> </label> <label> Title: <input value={person.artwork.title} onChange={handleTitleChange} /> </label> <label> City: <input value={person.artwork.city} onChange={handleCityChange} /> </label> <label> Image: <input value={person.artwork.image} onChange={handleImageChange} /> </label> <p> <i>{person.artwork.title}</i> {" by "} {person.name} <br /> (located in {person.artwork.city}) </p> <img src={person.artwork.image} alt={person.artwork.title} /> </> ); } export default App;
useImmer
を使用することで,nest したオブジェクトの更新も楽に行える。