Reputation: 13
I am using React and Redux to populate a user list. My component list all users from the redux store (the data is coming from a mongodb db, incase if you are curious). There is a checkbox to select-all users in the list, which upon checking should mark all checkboxes against all users. I think there are two approaches to do this.
Use a local-state (using useState hook) and dump all users in that state and then .map the user list. Clicking on "select all" checkbox will add a "is_checked" field for each users and during iteration check the user checkboxes only if the "is_checked" field is true.
Change the redux state and add the "is_checked" flag and do the rest as explain in point #1. (i dont think we should mutate the state)
Please advise.
import React from "react";
import { useSelector } from "react-redux";
const UserComponent = () => {
const users = useSelector((state) => state.users["users"]);
return (
<>
<label htmlFor="allUsers">
<input type="checkbox" /> Select All Users
</label>
{users.map((user) => (
<div key={user.id}>
<input type="checkbox" />
{user.name}
{user.email}
</div>
))}
</>
);
};
Upvotes: 0
Views: 653
Reputation: 11586
I think there is a third approach which would allow you to achieve what you are looking for, and avoid to taint the redux user store.
In fact, the users stored in redux serve as data cache for server values.
If at some point you need to send back a user value to an API, mutating that cache would introduce extraneous properties to those objects, which is not very clean. Basically you will have a mix of server and client UI concerns.
My suggestion is as follow:
Do no change the redux store with regards to how it stores the user values on the client.
Introduce a client-only state, called selectedUsers
which would contain an array or a set of user ID.
Code would be as follow:
(untested, just pseudo js code)
import React from "react";
import { useSelector } from "react-redux";
const UserComponent = () => {
const users = useSelector((state) => state.users["users"]);
const [selectedUsers, setSelectedUsers] = useState([]);
const allUsersSelected = selectedUsers.length === users.length
const toggleAllSelected = () =>
setSelectedUsers(val => val ? [] : users.map(u => u.id))
const makeHandleToggleUser = id => ev =>
setSelectedUsers(val => {
// if val includes id, return array less id
// otherwise return [...val, id]
}
return (
<>
<label htmlFor="allUsers">
<input type="checkbox" checked={allUsersSelected} onClick={toggleAllSelected} /> Select All Users
</label>
{users.map((user) => (
<div key={user.id}>
<input type="checkbox" onClick={makeHandleToggleUser(id)} checked={allUsersSelected || selectedUsers.includes(user.id)} />
{user.name}
{user.email}
</div>
))}
</>
);
};
Note: of if that selection needs to be made available to other components, you can perfectly put this value in redux instead of the component state, and use actions/selectors accordingly
Upvotes: 1
Reputation: 1677
It would be more convenient to add an isSelected
boolean for each user in the Redux store. So when you are setting the users list in the store, you can map it into a new array, add an isSelected
field to each user, which defaults to false
.
You will need to add an action to update the value of this flag from your component.
Upvotes: 0