Reputation: 681
I'm using nativebase checkbox and I'm mapping over the ListItem
, the issue is that when a user selects one row it changes the status
of ALL the rows. How do I get it to only change one row and not all of them during mapping
const [status, setStatus] = useState(false);
{Object.entries(
allEquipmentList.reduce(
(byType, item) => ({
...byType,
[item.type]: [...(byType[item.type] || []), item]
}),
{}
)
).map(([type, items]) =>
items.map((item, index) => {
return (
<>
<ListItem onPress={() => setStatus(!status)}>
<CheckBox checked={status} />
<Body>
<Text>{item.name}</Text>
</Body>
</ListItem>
</>
)
})
Upvotes: 0
Views: 260
Reputation: 1006
You have only one state, so, every checkbox are changing it. You need multiple states. How: put const [status, setStatus] = useState(false);
inside ListItem
and reimplement the switch logic.
You should move <CheckBox checked={status} />
inside ListItem
too for it to have access for your state.
Ex:
function ListItem({children}) {
const [status, setStatus] = useState(false);
return (
<ListItemInner onPress={() => setStatus(!status)}>
<CheckBox checked={status} />
<Body>
<Text>{children}</Text>
</Body>
</ListItemInner>
)
}
function SomeComponent(){
// ... your code here ...
return {Object.entries(
allEquipmentList.reduce(
(byType, item) => ({
...byType,
[item.type]: [...(byType[item.type] || []), item]
}),
{}
)
).map(([type, items]) => {
return items.map((item, index) => {
return <ListItem key={index}>{item.name}</ListItem>
}
})
}
This is the base code for log, but you still can't retrieve this information up in the tree
import { useEffect, useRef, useState } from "react";
import styled from "styled-components";
// just styles
const Label = styled.label`
display: flex;
align-items: center;
border: 1px solid #eee;
margin: 10px;
cursor: pointer;
`;
function ListItem({ children }) {
const [status, setStatus] = useState(false);
// useRef helps you to get HTML element (in this case)
// check this out: https://reactjs.org/docs/hooks-reference.html
const itemRef = useRef(null);
// useEffect allow you to see changes in your state. Just log outside could show you old states
useEffect(() => {
console.log(itemRef.current.innerText, status);
}, [status]);
return (
<Label>
<input
type="checkbox"
// let the browser handle the press, you just care about the change it made
onChange={() => setStatus(!status)}
checked={status}
/>
{/* basically a getElementById */}
<p ref={itemRef}>
<span>{children}</span>
</p>
</Label>
);
}
export default function App() {
const demoItems = ["foo", "doo", "boo"];
console.log("which items have been selected?");
return (
<div>
{demoItems.map((item, index) => (
<ListItem key={index}>{item}</ListItem>
))}
</div>
);
}
Here is the final code. Be aware, I don't think this is the best way of do it, but it works. Also, you should use some id
for that, not the name. Use it as learning process or hard-test it
Codesandbox of it: https://codesandbox.io/s/so-map-over-listitem-checkbox-only-to-change-for-one-row-react-native-kp12p?file=/src/App.js:133-1829
import { useEffect, useRef, useState } from "react";
import styled from "styled-components";
const Label = styled.label`
display: flex;
align-items: center;
border: 1px solid #eee;
margin: 10px;
cursor: pointer;
`;
function ListItem({ selected, setSelected, children }) {
const [status, setStatus] = useState(false);
const itemRef = useRef(null);
// outter control, uses a higher state
function handleChange() {
const el = itemRef?.current?.innerText;
const wasSelected = selected.includes(el);
console.log("el ->", el);
console.log("wasSelected ->", wasSelected);
if (wasSelected) {
setSelected((s) => s.filter((item) => item !== el));
} else {
setSelected((s) => [...s, el]);
}
// if the syntax of the state is weird to you, check it: https://stackoverflow.com/questions/42038590/when-to-use-react-setstate-callback
}
// just inner control, handles the visual update
useEffect(() => {
const el = itemRef?.current?.innerText;
setStatus(selected.includes(el));
}, [selected]);
return (
<Label>
<input type="checkbox" onChange={handleChange} checked={status} />
<p ref={itemRef}>
<span>{children}</span>
</p>
</Label>
);
}
export default function App() {
const demoItems = ["foo", "doo", "boo"];
const [selected, setSelected] = useState(["foo"]);
useEffect(() => {
console.log("which items have been selected?");
console.log(selected);
}, [selected]);
return (
<div>
{demoItems.map((item, index) => (
<ListItem key={index} selected={selected} setSelected={setSelected}>
{item}
</ListItem>
))}
</div>
);
}
Upvotes: 1