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

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;

Related

Make other block disappear when chose a value

How can I make other filter button disappear when picked 1 value.
Here is my code base:
const FilterBlock = props => {
const {
filterApi,
filterState,
filterFrontendInput,
group,
items,
name,
onApply,
initialOpen
} = props;
const { formatMessage } = useIntl();
const talonProps = useFilterBlock({
filterState,
items,
initialOpen
});
const { handleClick, isExpanded } = talonProps;
const classStyle = useStyle(defaultClasses, props.classes);
const ref = useRef(null);
useEffect(() => {
const handleClickOutside = event => {
if (ref.current && !ref.current.contains(event.target)) {
isExpanded && handleClick();
}
};
document.addEventListener('click', handleClickOutside, true);
return () => {
document.removeEventListener('click', handleClickOutside, true);
};
}, [isExpanded]);
const list = isExpanded ? (
<Form>
<FilterList
filterApi={filterApi}
filterState={filterState}
name={name}
filterFrontendInput={filterFrontendInput}
group={group}
items={items}
onApply={onApply}
/>
</Form>
) : null;
return (
<div
data-cy="FilterBlock-root"
aria-label={itemAriaLabel}
ref={ref}
>
<Menu.Button
data-cy="FilterBlock-triggerButton"
type="button"
onClick={handleClick}
aria-label={toggleItemOptionsAriaLabel}
>
<div>
<span>
{name}
</span>
<svg
width="8"
height="5"
viewBox="0 0 8 5"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.97291 0.193232C7.20854"
fill="currentColor"
/>
</svg>
</div>
</Menu.Button>
<div>
<div>
{list}
</div>
</div>
</div>
);
};
I am trying to achieve when I chose 1 value inside filter block the other block will disappear. Anyone have idea how can I work on this?
I am using React and Redux for this project
Thank you for helping me on this!!!!
Update:
Added parent component for FilterBlock.ks:
const FilterSidebar = props => {
const { filters, filterCountToOpen } = props;
const talonProps = useFilterSidebar({ filters });
const {
filterApi,
filterItems,
filterNames,
filterFrontendInput,
filterState,
handleApply,
handleReset
} = talonProps;
const filterRef = useRef();
const classStyle = useStyle(defaultClasses, props.classes);
const handleApplyFilter = useCallback(
(...args) => {
const filterElement = filterRef.current;
if (
filterElement &&
typeof filterElement.getBoundingClientRect === 'function'
) {
const filterTop = filterElement.getBoundingClientRect().top;
const windowScrollY =
window.scrollY + filterTop - SCROLL_OFFSET;
window.scrollTo(0, windowScrollY);
}
handleApply(...args);
},
[handleApply, filterRef]
);
const [selectedBlock, setSelectedBlock] = useState();
const filtersList = useMemo(
() =>
Array.from(filterItems, ([group, items], iteration) => {
const blockState = filterState.get(group);
const groupName = filterNames.get(group);
const frontendInput = filterFrontendInput.get(group);
if (selectedBlock) {
return (
<FilterBlock
key={group}
filterApi={filterApi}
filterState={blockState}
filterFrontendInput={frontendInput}
group={group}
items={items}
name={groupName}
onApply={handleApplyFilter}
initialOpen={iteration < filterCountToOpen}
iteration={iteration}
id={selectedBlock}
onSelected={setSelectedBlock}
/>
);
}
return (
<FilterBlock
key={group}
filterApi={filterApi}
filterState={blockState}
filterFrontendInput={frontendInput}
group={group}
items={items}
name={groupName}
onApply={handleApplyFilter}
initialOpen={iteration < filterCountToOpen}
iteration={iteration}
id={selectedBlock}
onSelected={setSelectedBlock}
/>
);
}),
[
filterApi,
filterItems,
filterNames,
filterFrontendInput,
filterState,
filterCountToOpen,
handleApplyFilter
]
);
return (
<div className="container px-4 mx-auto">
<Menu
as="div"
className="my-16 justify-center flex flex-wrap py-5 border-y border-black border-opacity-5"
>
{filtersList}
</Menu>
</div>
);
};
console.log(filterItems) and it gave me this output:
Map(3) {'markforged_printer_type' => Array(3),
'markforged_material_filter' => Array(7), 'markforged_parts_filter' =>
Array(7)} [[Entries]] 0 : {"markforged_printer_type" => Array(3)} 1 :
{"markforged_material_filter" => Array(7)} 2 :
{"markforged_parts_filter" => Array(7)}
Updated Answer
From the changes you provided, you are using useMemo() and useCallback(). Those kinds of optimizations in general are not necessary to be made or even decrease performance in some cases. Check this article from Kent C. Dodds (others can be easily found about the theme) to explain some issues with it.
About the changes, as a suggestion, you could use the .map()/.filter() functions instead Array.from().
You are splitting logic about rendering different components with the useMemo(), and this could be changed into one component instead of this whole logic inside the Parent component. (For my suggestion this will be not the case)
As a guide to your code, you could use something like this:
const FilterSidebar = ({ filters, filterCountToOpen }) => {
// here you have the state to control if there is a block selected
const [selectedGroup, setSelectedGroup] = useState();
const {
// only those are needed for this example
filterItems,
handleApplyFilter
} = useFilterSidebar({ filters });
return (
<div className="container px-4 mx-auto">
<Menu
as="div"
className="my-16 justify-center flex flex-wrap py-5 border-y border-black border-opacity-5"
>
{filterItems.map(([group, items], iteration) => {
const groupName = filterNames.get(group);
if (selectedGroup !== null && selectedGroup !== groupName) {
// returning null here should not render anything for this list item
return null;
}
return (
<FilterBlock
// pass all your extra props here
// but the important one is the `onApply`
onApply={(...args) => {
setSelectedGroup((prev) => prev !== null ? null : groupName);
return handleApplyFilter(...args);
}}
/>
);
}}
</Menu>
</div>
);
};
If you see any null on your screen, you could use first the .filter() and then the .map() or combine both with a single .reduce(). It should be something like this:
{filterItems
.filter(([group, items]) => selectedGroup === null || selectedGroup === filterNames.get(group))
.map(([group, items], iteration) => {
const groupName = filterNames.get(group);
return (
<FilterBlock
// pass all your extra props here
// but the important one is the `onApply`
onApply={(...args) => {
setSelectedGroup((prev) => prev !== null ? null : groupName);
return handleApplyFilter(...args);
}}
/>
);
}}
With your update, it is possible to see that you can select by the group (instead of the block which it was called before). Also, you can just add a little change to your onApply prop and that will save and re-render the list. If the selectedGroup is already there, removing the filter will show the other sections. Eventually, you'll need to trim this logic to accommodate other things such as selecting more than one filter and checking for that and so on.
Original Answer
From what you described I'm assuming what you want is: You have 3 FilterBlocks on your screen. Once a user selects one checkbox inside one opened "select" (that you are calling FilterBlock), you want the other FilterBlocks disappear from the screen and just the single FilterBlock with the selected option to stay at the screen (the other 2 will be hidden).
If that's your case, there are some possible options to achieve that but the easiest one is controlling this on a Parent Component: You can pass a prop from the parent component named something like onSelected, give an id to each FilterBlock, and when one filter is selected inside, you trigger that callback with the id from that FilterBlock.
const Parent = () => {
const [selectedBlock, setSelectedBlock] = useState();
if (selectedBlock) {
return <FilterBlock id={selectedBlock} onSelected={setSelectedBlock} />
}
return (
<>
<FilterBlock id="filter-block-1" onSelected={setSelectedBlock} />
<FilterBlock id="filter-block-2" onSelected={setSelectedBlock} />
<FilterBlock id="filter-block-2" onSelected={setSelectedBlock} />
</>
)
}
const FilterBlock = ({ id, onSelected }) => (
return (
<>
<button onClick={() => onSelected(id)}>Select filter block {id}</button>
<button onClick={() => onSelected()}>Unselect filter block {id}</button>
</>
);

Why changes in Checkbox's value are not displayed?

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

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.

Auto deselect the "first selected item" after selecting two items in Material UI

I am new to web dev and I cannot figure out how to do this task. Here is the problem statement.
Given three checkboxes If select box1 and then box2 it should show marked. Now if I select box3 then box1 should be automatically unchecked and must reflect in UI.
Here is the code sample I'm trying.
export default function CheckboxesGroup() {
const classes = useStyles();
const [state, setState] = React.useState({
gilad: true,
jason: false,
antoine: false,
});
const handleChange = (event) => {
setState({ ...state, [event.target.name]: event.target.checked });
};
const { gilad, jason, antoine } = state;
const error = [gilad, jason, antoine].filter((v) => v).length !== 2;
return (
<div className={classes.root}>
<FormControl required error={error} component="fieldset" className={classes.formControl}>
<FormLabel component="legend">Pick two</FormLabel>
<FormGroup>
<FormControlLabel
control={<Checkbox checked={gilad} onChange={handleChange} name="gilad" />}
label="Gilad Gray"
/>
<FormControlLabel
control={<Checkbox checked={jason} onChange={handleChange} name="jason" />}
label="Jason Killian"
/>
<FormControlLabel
control={<Checkbox checked={antoine} onChange={handleChange} name="antoine" />}
label="Antoine Llorca"
/>
</FormGroup>
<FormHelperText>You can display an error</FormHelperText>
</FormControl>
</div>
);
}
or you can goto https://github.com/mui-org/material-ui/blob/master/docs/src/pages/components/checkboxes/CheckboxesGroup.js
Here is the output: https://y9q9rx--run.stackblitz.io
if you have only 3 options then you can set particular conditions for checked items
By changing the handleChange function like this
const handleChange = (event) => {
const {name, checked} = event.target;
let newState = {};
if([gilad, jason, antoine].filter((v) => v).length === 2 && checked === true){
if(name === 'gilad'){
newState = {jason: false};
} else if(name === 'jason'){
newState = {gilad: false};
} else if(name === 'antoine'){
newState = {gilad: false};
}
}
setState({ ...state,...newState, [name]: checked });
};
Not sure if I understand your question correctly, if I misunderstood the question, please tell me. As far as I understand, you're trying to enforce the user to select at most 2 boxes, further selection will cancel the earliest selection.
For this problem, we can get an insight that the order of selection matters, but the data structure you are using does not reflect this. You are now using
{
gilad: true,
jason: false,
antoine: false,
}
as the state, which does not contains the information of the order of checking. In this case, I would suggest using an array instead, which can keep track of the ordering information more easily.
const [state, setState] = React.useState(['gilad']);
const handleChange = (event) => {
const { name, checked } = event.target;
// handles unchecking the checked box
if (!checked) {
setState(prev => prev.filter(n => n !== name));
} else {
const stateWithNewName = [...state, name];
// only take the latest two names
setState(stateWithNewName.slice(-2));
}
};
const error = state.length !== 2;
for the Checkbox component,
// notice the modification for the `checked` prop
<Checkbox checked={state.includes('gilad')} onChange={handleChange} name="gilad" />

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>

Categories

Resources