タイトルそのままです。
const initialState = {
'user1': true,
'user2': true,
...
};
const [user, setUser] = React.useState(initialState);
というようなstateがあったとして、例えばクリックされたユーザ番号のvalueを変えたいとします。で、すべてにチェックをチェックしたら、全チェックボックスがチェックされるようにしたい。
なので、1個チェックしたときは、Object内のチェックしたkey, valueペアだけ返してstateを変更し、全チェックしたときは、すべてのObject内のkeyに対してvalueだけ変更してObjectを丸々返すようにしたかったのですが、Objectを丸々受け取ってそれをReactのstateに渡すにはどうしたらよいのかが地味にわからなくて時間がかかりました。
Codesandboxはこちら
export default function User({ users, handleChange }) {
function getUserObject(users, key, value) {
if (key === null && value === null) {
Object.entries(users).map(([key, val]) => {
users[key] = !val;
});
return users;
}
return { [key]: !value };
}
return (
<>
<input
type="checkbox"
onChange={() => handleChange(getUserObject(users, null, null))}
/>
<label>check all</label>
{users ? (
Object.entries(users).map(([key, value]) => {
return (
<div>
<input
type="checkbox"
checked={value}
onChange={() => handleChange(getUserObject(users, key, value))}
/>
<label>{key}</label>
</div>
);
})
) : null}
</>
);
}
export default function App() {
const initialState = {
user1: false,
user2: false
};
const [users, setUser] = React.useState(initialState);
return (
<div className="App">
<User
users={users}
handleChange={(obj) =>
setUser((state) => Object.assign({ ...state }, obj))
}
/>
</div>
);
}
ポイント1: Object.assignならObjectそのままを渡せる
ポイントはここです。Object.assign
を使い、さらにstate
は...state
ではなくて、{...state}
で渡す必要があります。ちなみにObjectを変更するやり方は色々あって、Object.assign
でなくても{...state, key: val}
でも行けるようですが、そうすると、Objectはそのまま渡せません。
setUser((state) => Object.assign({ ...state }, obj))
ポイント2: key, valueを変数名で受け取ってObjectにしたい
全てのkey, valueに対してvalueの値を変更したい場合はこう。
Object.entries(users).map(([key, val]) => {
users[key] = !val;
});
一個のkey, valueペアについて単体でObjectを作って返したいとき。
こうする。ミソは[key]
ここ。 key: !value
だと、key名がそのままkey
という名前のObjectになってしまいます...ので、[key]
にしないと認識されません。
{ [key]: !value };
コード例自体は別のところで使用したものをこれを書くためにサクッと書いたので、まあちょっとおかしなところありますが...