Why changes in Checkbox's value are not displayed? - javascript

I'm using react and material ui. I need to get all checkboxes in a list checked by checking one checkbox in a parent component (i.e. I need to select all). I pass down the correct value of the parent checkbox via props, but that doesn't trigger visual changes in its children, even though their values do change to 'true'. I'm sure that the values are correct, because I tried logging them to the console.
Here's the parent:
const [checkboxValue, setCheckboxValue] = useState(false)
<Checkbox value={checkboxValue} onChange={e => setCheckboxValue(e.target.checked)}/>
{elements.map(element => (<Element selectAll={checkboxValue}/>))}
And here's the child:
function Element(props) {
const [checkboxValue, setCheckboxValue] = useState(false)
useEffect(() => {
setCheckboxValue(props.selectAll)
}, [props.selectAll])
return (
<Checkbox value={checkboxValue} onChange={e => setCheckboxValue(e.target.checked)}/>)
}

you are using value instead of checked
function SingleCheckBox({ selectAll }) {
const [checkboxValue, setCheckboxValue] = useState(selectAll);
useEffect(() => {
setCheckboxValue(selectAll);
}, [selectAll]);
return (
<Checkbox
checked={checkboxValue}
onChange={(e) => setCheckboxValue(e.target.checked)}
/>
);
}
export default function App() {
const [checkboxValue, setCheckboxValue] = useState(false);
return (
<div className="App">
<Checkbox
checked={checkboxValue}
onChange={(e) => {
setCheckboxValue(e.target.checked);
}}
/>
<div>
<h2>Other checkboxes</h2>
<SingleCheckBox selectAll={checkboxValue} />
</div>
</div>
);
}
https://codesandbox.io/s/angry-buck-yp7f3z?file=/src/App.js

Related

Get all the fields' values that display on screen in ReactJs

In my ReactJs app I want to get only the values of the fields that shows to the screen, for example I have some radio groups and one group will be hide when I choose specific value like the code below:
const App = () => {
const [value, setValue] = useState();
const [value2, setValue2] = useState();
const onChange = (e) => {
setValue(e.target.value);
};
const onChange2 = (e) => {
setValue2(e.target.value);
};
return (
<div className='container'>
<div>
<Radio.Group value={value} onChange={onChange}>
<Radio value={1}>yes</Radio>
<Radio value={2}>no</Radio>
</Radio.Group>
</div>
{value === 1 ? null : (
<div>
<Radio.Group value={value2} onChange={onChange2}>
<Radio value={3}>yes</Radio>
<Radio value={4}>no</Radio>
</Radio.Group>
</div>
)}
</div>
);
};
as you can see in my simple code, when the value at the first radio groud is '1' the second radio group will be hide. but, if I choose in the second radio group some value (3 or 4) and then select in the first radio group the value '1', the value in the second still will be what I choose before.
This case is simple to reset one field or ignore it, but if I have a form with 20 fields, I want to get only the fields that shows on screen, even if I did not reset the hide fields.
There is a way to get all the values that render in 'container' div or better way to do it?
Try this
// This means "whenever the second group becomes hidden, reset its state to default"
useEffect(() => {
const secondIsHidden = value === 1;
if (secondIsHidden) setValue2(undefined); // Or whatever
}, [value]);
I created a state and with each key, i set the boolean value. If its not-hidden, i set false otherwise true.
const App = () => {
const [value, setValue] = useState();
const [value2, setValue2] = useState();
const [radioValues, setRadioValues] = useState({ 1: false, 2: false, 3: false, 4: false });
const onChange = (e: any) => {
setRadioValues((prev) => {
let newValues: any = {};
Object.entries(prev).forEach((item: any) => {
newValues[parseInt(item[0])] = e.target.value === 1 ? [3, 4].includes(parseInt(item[0])) : false;
});
return newValues;
});
setValue(e.target.value);
};
const onChange2 = (e: any) => {
setValue2(e.target.value);
};
console.log(Object.keys(radioValues).filter((key) => !radioValues[key]));
return (
<div className='container'>
<div>
<Radio.Group value={value} onChange={onChange}>
<Radio value={1}>yes</Radio>
<Radio value={2}>no</Radio>
</Radio.Group>
</div>
{value === 1 ? null : (
<div>
<Radio.Group value={value2} onChange={onChange2}>
<Radio value={3}>yes</Radio>
<Radio value={4}>no</Radio>
</Radio.Group>
</div>
)}
</div>
);
};
export default App;

How to keep already chosen value in ReactJS

I have following code.
What I'm trying to do is, in the first step, select one of the elements, store it in my state, and in the last step, console.log all my data. Also, the user can go from the last step to the first and change what he chose before. But the problem is that I can't save what the user selects for the first time.
For example, if the user selects the second one, and then on the last step they go back, then the first one is displayed as selected. How can I fix this?
here is my code
App.js
const [current, setCurrent] = useState(0);
const [data, setData] = useState({
firstName: "AAA",
lastName: "BBB",
age: 26
});
const steps = [
{
content: (
<PackageChoose setCurrent={setCurrent} data={data} setData={setData} />
),
id: 0
},
{
content: <LastStep setCurrent={setCurrent} data={data} />,
id: 1
}
];
return (
<div className="App">
<div>{steps[current].content}</div>
</div>
);
packageChoose (or first step)
const PackageChoose = ({ setCurrent, data, setData }) => {
const [selected, setSelected] = useState(1);
const [packageType, setPackageType] = useState(data.package || "choice");
return (
<div>
<div
onClick={() => {
setPackageType("choice");
setData({ ...data, packageType: packageType });
}}
>
<SelectCard
id={1}
selected={selected}
onSelect={setSelected}
text="text 1"
/>
</div>
<div
onClick={() => {
setPackageType("select");
setData({ ...data, packageType: packageType });
}}
>
<SelectCard
id={2}
selected={selected}
onSelect={setSelected}
text="text 2"
/>
</div>
<button onClick={() => setCurrent(1)}>Next</button>
</div>
);
};
Last step
const LastStep = ({ setCurrent, data }) => {
return (
<div>
LastStep
<button
onClick={() => {
setCurrent(0);
}}
>
Previous
</button>
<button onClick={() => console.log("data===>", data)}> submit </button>
</div>
);
};
Selected Card reusable component
const SelectCard = ({ id, selected, onSelect, text }) => {
const myClassName =
id === selected
? Styles.selectCardWrapperActives
: Styles.selectCardWrapper;
return (
<div className={classNames(myClassName)} onClick={() => onSelect(id)}>
<div> {text} </div>
</div>
);
};
Please help me to fix this problem.
You can move the selected state in PackageChoose to App level.
In App.js define the selected state and pass as props.
export default function App() {
const [selected, setSelected] = useState(1);
...
...
<PackageChoose
...
...
selected={selected}
setSelected={setSelected}
/>
}
In PackageChoose use the props passed above and remove the local selected state.
const PackageChoose = ({ setCurrent, data, setData, setSelected, selected }) => {
You need to update the packageType inside onClick handler. Since setState calls are batched and enqueued inside event handler and state updates may be asynchronous. You can't access the packageType state immediately after setting it.
PackageChoose.js
Card 1
onClick={() => setData({ ...data, packageType: "choice" })}
Card 2
onClick={() => setData({ ...data, packageType: "select" })}
set the packageType directly on data.

How to filter an incoming prop in React?

I'm trying to build a search bar using hooks but the props that I pass into my functional component gives me error and the output is never loaded. I pass the opt props from options.js to Filter.js. Here's the code:
Options.js
import Filter from './Filter'
import Option from './Option'
export default (props) => {
return (
<div>
<button onClick={props.handleDeleteOptions}>Remove All</button>
{props.options.length === 0 && <p>Please add option to get Started!</p>}
{
props.options.map((x) => <Option key={x} optionText={x}
handleDeleteOption={props.handleDeleteOption}
/>)
}
<Filter opt={props.options} />
</div>
);
}
Filter.js
export default (props) => {
const [option, setOption] = React.useState(props);
const [search, setSearch] = React.useState("");
return (
<div>
<input
onChange={e => {
const test = props.opt.filter(o => {
return o.toLowerCase().includes(e.target.value.toLowerCase());
});
console.log("Option: ", test);
setOption(test);
setSearch(e.target.value);
}}
type="text"
value={search}
/>
{props.opt.map(o => (
<p key={o}>{o}</p>
))}
</div>
)
};
In you Filter.js you are mapping directly the props.opt. This doesn't change when you do the filter. What changes is the state. There are two things you need to change for this to work:
Fix default option state: const [option, setOption] = React.useState(props.opt);
Fix the options map. Instead of props.opt, use option from the state.

ReactJS Change the JSX from onClick function

i have a backend api build on nodejs. in the code the api return some categories array.
i ran .map() function on the array to display the array in the JSX.
after that i added a checkBox to each object inside the array.
so what im trying to do is if the checkbox is true its will added another h1 Element (JSX).
Only to the object i clicked on the checkbox.
i tryied to add "status" props and make it false or true and then catch it with onClick e.target.status?
"YES" : "NO"
also, i tried to added checkBox useState and make it true or false . and its work. but not as i want
its display Yes or No to the all objects and not only to the on i clicked on.
const Category = ({ history }) => {
const dispatch = useDispatch()
const user = useSelector((state) => state.waiter)
const selectedCategory = useSelector((state) => state.selectedTable)
const [currectCategory, setCurrectCategory] = useState([])
const [categoryName, setCategoryName] = useState("")
const [categoryIMG, setCategoryIMG] = useState("not found")
const [checkBox, setCheckBox] = useState("false")
useEffect(() => {
if (!user.name) {
history.push('/login')
} else {
(async () => {
const res = await fetch('http://localhost:1000/categories/' + selectedCategory)
const data = await res.json()
setCurrectCategory(data.CurrectCountry.subcategories.map(sc => sc.name))
setCategoryName(data.CurrectCountry.name)
setCategoryIMG(data.CurrectCountry.img)
})()
}
}, [user])
const goBack = () => {
dispatch({
type: 'ALL_CATEGORIES'
})
history.push('/login')
}
const handleCheck = (e) => {
setCheckBox(e.target.checked.toString())
console.log(e.target.checked)
}
return (
<>
<Button className="LogButton" color="secondary" onClick={goBack}>back</Button>
<div className="SingleCategory">
<h1>{categoryName}</h1>
<ListGroup>
{currectCategory.map(category => {
return (
<Row className="Col-padd" key={category}>
<div>
<InputGroup className="mb-3">
<b className="ItemName"> {category} </b>
<img src={categoryIMG} height="100" width="100" ></img>
<FormCheck id={category} className="Checkbox" onChange={handleCheck}></FormCheck>
{checkBox == "true" ? <b>yes</b> : <b>No</b>}
</InputGroup>
</div>
</Row>
)
})}
</ListGroup>
</div>
</>
)
}
Thanks for help !!
You are only creating a single value for the checkbox. If you want to show for all the checkbox, if you have to track the value for each checkbox shown below,
const [checkBox, setCheckBox] = useState({}); // checkBoxName: value
const handleCheck = (e) => {
setCheckBox((prev) => {...prev, [e.target.name]: e.target.value};
}
{!!checkBox['name'] === true ? <b>yes</b> : <b>No</b>}
//change the attribute according to your implementation.
Your problem is that you're just creating a single value for the checkbox and not separating the individual checkboxes. You could solve this in many different ways, but you would be well served by extracting the code for your checkbox to a separate component.
const Checkbox = ({ category, categoryIMG }) => {
const [isChecked, setIsChecked] = useState(false);
const handleCheck = () => {
setIsChecked((prevState) => !prevState);
};
return (
<Row className="Col-padd" key={category}>
<div>
<InputGroup className="mb-3">
<b className="ItemName"> {category} </b>
<img src={categoryIMG} height="100" width="100"></img>
<FormCheck id={category} className="Checkbox" onChange={handleCheck}></FormCheck>
{isChecked == 'true' ? <b>yes</b> : <b>No</b>}
</InputGroup>
</div>
</Row>
);
};
With a separate checkbox component like above you could instantiate it like this in the map:
<ListGroup>
{currectCategory.map((category) => (
<Checkbox category={category} categoryIMG={categoryIMG} />
))}
</ListGroup>

How to pass HTML attributes to child component in React?

I have a parent and a child component, child component has a button, which I'd like to disable it after the first click. This answer works for me in child component. However the function executed on click now exists in parent component, how could I pass the attribute down to the child component? I tried the following and it didn't work.
Parent:
const Home = () => {
let btnRef = useRef();
const handleBtnClick = () => {
if (btnRef.current) {
btnRef.current.setAttribute("disabled", "disabled");
}
}
return (
<>
<Card btnRef={btnRef} handleBtnClick={handleBtnClick} />
</>
)
}
Child:
const Card = ({btnRef, handleBtnClick}) => {
return (
<div>
<button ref={btnRef} onClick={handleBtnClick}>Click me</button>
</div>
)
}
In general, refs should be used only as a last resort in React. React is declarative by nature, so instead of the parent "making" the child disabled (which is what you are doing with the ref) it should just "say" that the child should be disabled (example below):
const Home = () => {
const [isButtonDisabled, setIsButtonDisabled] = useState(false)
const handleButtonClick = () => {
setIsButtonDisabled(true)
}
return (
<>
<Card isDisabled={isButtonDisabled} onButtonClick={handleButtonClick} />
</>
)
}
const Card = ({isDisabled, onButtonClick}) => {
return (
<div>
<button disabled={isDisabled} onClick={onButtonClick}>Click me</button>
</div>
)
}
Actually it works if you fix the typo in prop of Card component. Just rename hadnlBtnClick to handleBtnClick
You don't need to mention each prop/attribute by name as you can use javascript Object Destructuring here.
const Home = () => {
const [isButtonDisabled, setIsButtonDisabled] = useState(false)
const handleButtonClick = () => {
setIsButtonDisabled(true)
}
return (
<>
<Card isDisabled={isButtonDisabled} onButtonClick={handleButtonClick} />
</>
)
}
const Card = (props) => {
return (
<div>
<button {...props}>Click me</button>
</div>
)
}
You can also select a few props and use them differently in the child components. for example, see the text prop below.
const Home = () => {
const [isButtonDisabled, setIsButtonDisabled] = useState(false)
const handleButtonClick = () => {
setIsButtonDisabled(true)
}
return (
<>
<Card text="I'm a Card" isDisabled={isButtonDisabled} onButtonClick={handleButtonClick} />
</>
)
}
const Card = ({text, ...restProps}) => {
return (
<div>
<button {...restProps}>{text}</button>
</div>
)
}

Categories

Resources