I've a problem about Chakra UI's select component. It's somehow doesn't reset their value. Also, when I press the button, Select component doesn't render with new value
https://codesandbox.io/s/compassionate-shockley-f9chz?file=/src/app.tsx:518-863
export const App = (): JSX.Element => {
const [value, setValue] = React.useState("in");
const onClick = () => {
setValue(null);
};
console.log("App value", value);
return (
<div>
<Select options={selectionOptions} value={value} />
<Button onClick={onClick}> click </Button>
</div>
);
};
const [value, setValue] = useState(propValue);
useEffect(() => {
if (handleChange) {
handleChange(value);
}
}, [handleChange, value, setValue]);
<Select
{...rest}
value={value}
onChange={(event) => {
setValue(event?.target?.value);
}}
>
{options.map(({ value: currentValue, name }, index) => (
<option key={index} value={currentValue}>
{name}
</option>
))}
</Select>
Bumped into the exact problem. I fixed it by resetting the value with setValue("") instead of setValue(null).
Related
I need a search box in React that opens a dropdown as soon as an entry is made. The dropdown should contain buttons that can trigger an event.
The problem is that the dropdown has to disappear as soon as another input box is used in the application.
I could also implement this, but now I have the problem that the event of the button in question is not triggered because the focus of the input field is lost beforehand as soon as I press the button. As a result, the dropdown disappears and the event is never triggered.
This is roughly my Searchbox Component:
import React, { useState } from 'react';
import Dropdown from './Dropdown';
function Search(props) {
const [focused, setFocused] = useState(false);
const inputHandler = (params) => {
if (params.length > 0)
props.apiCall(params);
}
const buttonHandler = (id) => {
console.log(id);
}
return (
<>
<input
type="text"
placeholder="Suchen.."
onChange={(event) => inputHandler(event.target.value)}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)} // Problem area
/>
{
focused === true && props.apiData.length > 0 ?
props.apiData.map((mappedData, key) => {
return (
<Dropdown
key={key}
id={mappedData.id}
name={mappedData.name}
/*
even more Data
*/
buttonHandler={buttonHandler}
/>
)
})
: null
}
</>
)
}
export default Search;
This is my Dropdown Component:
import React from 'react';
function Dropdown(props) {
return (
<ul key={props.id}>
<li>{props.name}</li>
<li>even more data</li>
<li>
<input
type="button"
value="Select"
onClick={() => {
props.buttonHandler(props.id)
}}
/>
</li>
</ul>
)
}
export default Dropdown;
To resolve this issue blur event in Search Component can be handled with delay.
function Search(props) {
const [focused, setFocused] = useState(false);
const inputHandler = (params) => {
if (params.length > 0) props.apiCall(params);
};
const buttonHandler = (id) => {
console.log(id);
};
return (
<>
<input
type="text"
placeholder="Suchen.."
onChange={(event) => inputHandler(event.target.value)}
onFocus={() => setFocused(true)}
- onBlur={() => setFocused(false)} // Problem area --> remove this line
+ onBlur={() => setTimeout(()=> setFocused(false),500)} // ---> delay added
/>
{focused === true && props.apiData.length > 0
? props.apiData.map((mappedData, key) => {
return (
<Dropdown
key={key}
id={mappedData.id}
name={mappedData.name}
/*
even more Data
*/
buttonHandler={buttonHandler}
/>
);
})
: null}
</>
);
}
I want to add to Chip an startIcon={<Icon />}
when click on a Chip.
The state of the icon is managed by chipsState.
In this code,
the state of all chips would change.
How can I change only the chipsState of the element that is clicked?
In this code, the state of all chips will change.
How can I change only the chipsState of the element that is clicked?
const Modal:React.FC<Props>= (props) => {
const {modalData} = props;
const [chipsState, setChipsState] = useState(false);
const onChipClick = (element:any) => {
setChipsState(chipsState => !chipsState);
}
return (
<div>
{
modalData.symtoms.map((element:any, index:number) => (
<div key={index}>
<Chip onClick={() => onChipClick(element)} startIcon={chipsState && <Icon />}>{element.description}</Chip>
</div>
))}
</div>
);
}
export default Modal;
To handle local state (and better testing), you should create a new custom Chip component with dedicated chipState.
interface CustomChipProps {
description: string
}
const CustomChip = (props: CustomChipProps) => {
const [chipState, setChipState] = useState(false);
return <Chip onClick={() => setChipState(prev => !prev)} startIcon={chipState && <Icon />}>{props.description}</Chip>;
}
const Modal:React.FC<Props>= (props) => {
const {modalData} = props;
return (
<div>
{
modalData.symtoms.map((element:any, index:number) => (
<div key={index}>
<CustomChip description={element.description} />
</div>
))}
</div>
);
}
export default Modal;
You can achieve your desired output by changing chipState state from boolean to object.
So first let's change to object state instead of boolean
const [chipsState, setChipsState] = useState({});
Now we will change onChipClick function to change value of selected chip state
const onChipClick = (element:any) => {
setChipsState({...chipsState, chipsState[element]: !chipsState[element]});
}
And finally we will read correct value of each chipsState element.
<Chip onClick={() => onChipClick(element)} startIcon={chipsState[element] && <Icon />}>{element.description}</Chip>
You can try like the following
import React, { useState, useCallback } from "react";
import ReactDOM from "react-dom";
import { Grid, Row } from "react-flexbox-grid";
const ChipSet = ({ symtomsData }) => {
const data = symtomsData.map((symtom) => ({ ...symtom, isSelcted: false }));
const [chipSets, setChipSets] = useState(data);
const onSelectChipSet = useCallback(
(e, index) => {
const updatedChipSets = chipSets.map((chip, i) =>
i === index ? { ...chip, isSelcted: e.target.checked } : chip
);
setChipSets(updatedChipSets);
},
[chipSets]
);
console.log("chipSets", chipSets);
return (
<div>
<h1>Symtoms Data</h1>
{chipSets.map((x, i) => (
<div key={i}>
<label>
<input
onChange={(e) => onSelectChipSet(e, i)}
type="checkbox"
value={x.isSelcted}
/>
{x.description}
</label>
</div>
))}
</div>
);
};
class App extends React.Component {
render() {
const symtomsData = [
{
description: "mild"
},
{
description: "cold"
}
];
return (
<Grid>
<Row>
<ChipSet symtomsData={symtomsData} />
</Row>
</Grid>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
I have the following code
const Component = () => {
const [Clicked, setClicked] = useState(false);
const handleClick = (prop, e) => {
console.log(prop);
}
return (
<div>
{arrayCard.map((item, i) => {
return (<Card
onClick={(e) => handleClick(e.target.getAttribute('akey'), e)}
src={item}
akey={i} />)
})}
</div>
);
and This is my card component
import React from 'react';
import Image from 'next/image'
const Card = (props) => {
return (
<div>
<button onClick={props.onClick} >
<Image
src={props.src}
alt="Card Pic"
width={80}
height={80}
/>
</button>
</div>
);
}
As you can see, I have a .map function which gets values from an array and makes card elements. What I want to do is get the specific element being clicked so I thought if I pass a key value I can get the exact element being clicked.
The problem is when I want to get the value of the key prop with the e.target.getAttrinute('akey') returns null. If I do it with the src it works.
I hope you can help me with this. Thanks!
You can just pass the item's index (that's what usually done):
const Component = (props) => {
const handleClick = (index, e) => {
console.log(index);
};
return (
<div>
{props.arrayCard.map((item, index) => {
return (
<Card key={index} onClick={(e) => handleClick(index, e)} src={item} />
);
})}
</div>
);
};
I'm struggling to create multiple Checkbox filtering in React with Material-UI. The difficult is that checkbox options are created dynamically from received data and should be split by each type of Select components
But I can't properly create states to manage them in the App.
Any ideas how to do it correctly?
Options Component
function filterDuplicate(arr) {
return arr.filter((elem, index, array) => array.indexOf(elem) === index);
}
export default function Options({ stat }) {
const [form, setForm] = useState({
publicationType: "",
termType: "",
reportGroup: "",
reportState: "",
reportFormat: ""
});
const publicationTypes = filterDuplicate(
stat.map((data) => data.publicationType)
);
const termTypes = filterDuplicate(stat.map((data) => data.termType));
const reportGroups = filterDuplicate(stat.map((data) => data.reportGroup));
const reportStates = filterDuplicate(stat.map((data) => data.reportState));
const reportFormats = filterDuplicate(stat.map((data) => data.reportFormat));
function handleSubmit(e) {
e.preventDefault();
console.log(form);
}
return (
<>
<form onSubmit={handleSubmit} className="options">
<Select type="Publication type" options={publicationTypes} />
<Select type="Term type" options={termTypes} />
<Select type="Report group" options={reportGroups} />
<Select type="Status" options={reportStates} />
<Select type="File Type" options={reportFormats} />
<Button variant="contained" color="secondary" type="submit">
RESET
</Button>
</form>
</>
);
}
Options.propTypes = {
stat: PropTypes.arrayOf(PropTypes.shape({})).isRequired
};
Select Component
export default function Select({ type, options }) {
const [check, setCheck] = useState([]);
const [value, setValue] = useState("");
const classes = useStyles();
const handleChange = (e) => {
if (e.target.checked) {
setCheck([...check, e.target.value]);
} else {
setCheck(check.filter((id) => id !== e.target.value));
}
const str = check.join(", ");
setValue(str);
};
return (
<>
<FormControl className={classes.formControl}>
<InputLabel id="select-label" className={classes.label}>
{type}
</InputLabel>
<MaterialSelect labelId="select-label" id="input-select">
{options.map((option) => (
<Checkbox option={option} key={option} onChange={handleChange} />
))}
</MaterialSelect>
</FormControl>
</>
);
}
Checkbox Component
const Checkbox = React.forwardRef(({ option, onChange }, ref) => {
return (
<div ref={ref}>
<FormControlLabel
control={<MaterialCheckbox onChange={onChange} color="primary" />}
label={option}
value={option}
/>
</div>
);
});
Checkbox.propTypes = {
option: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired
};
export default Checkbox;
https://codesandbox.io/s/checkbox-filter-vqex7?file=/src/Options.js:0-1593
The problem is you only pass data from parent to child. On App.js they are two separate components. You should write a function on App.js that connects these two components.
Your App.js
export default function App() {
const [stat, setState] = useState([]);
useEffect(() => {
setState(data);
}, []);
return (
<div className="App">
<div>
<div>
<h1>Information</h1>
</div>
<Options stat={stat} />
<Data info={stat} />
</div>
</div>
);
}
App.js when filtering method added
export default function App() {
const [stat, setState] = useState([]);
useEffect(() => {
setState(data);
}, []);
const handleFilter = (selectedValue) => {
console.log(selectedValue);
//apply filtering here and setState again
}
return (
<div className="App">
<div>
<div>
<h1>Information</h1>
</div>
<Options stat={stat} handleFilter={handleFilter} />
<Data info={stat} />
</div>
</div>
);
}
Options.js
export default function Options({ stat, handleFilter }) {
const [form, setForm] = useState({
publicationType: "",
termType: "",
reportGroup: "",
reportState: "",
reportFormat: ""
});
const publicationTypes = filterDuplicate(
stat.map((data) => data.publicationType)
);
const termTypes = filterDuplicate(stat.map((data) => data.termType));
const reportGroups = filterDuplicate(stat.map((data) => data.reportGroup));
const reportStates = filterDuplicate(stat.map((data) => data.reportState));
const reportFormats = filterDuplicate(stat.map((data) => data.reportFormat));
function handleSubmit(e) {
e.preventDefault();
console.log(form);
}
return (
<>
<form onSubmit={handleSubmit} className="options">
<Select
type="Publication type"
options={publicationTypes}
handleFilter={handleFilter}
/>
<Select
type="Term type"
options={termTypes}
handleFilter={handleFilter}
/>
<Select
type="Report group"
options={reportGroups}
handleFilter={handleFilter}
/>
<Select
type="Status"
options={reportStates}
handleFilter={handleFilter}
/>
<Select
type="File Type"
options={reportFormats}
handleFilter={handleFilter}
/>
<Button variant="contained" color="secondary" type="submit">
RESET
</Button>
</form>
</>
);
}
Select.js
export default function Select({ type, options, handleFilter }) {
const [check, setCheck] = useState([]);
const [value, setValue] = useState("");
const classes = useStyles();
const handleChange = (e) => {
if (e.target.checked) {
handleFilter(e.target.value);
setCheck([...check, e.target.value]);
} else {
setCheck(check.filter((id) => id !== e.target.value));
}
const str = check.join(", ");
setValue(str);
};
return (
<>
<FormControl className={classes.formControl}>
<InputLabel id="select-label" className={classes.label}>
{type}
</InputLabel>
<MaterialSelect labelId="select-label" id="input-select">
{options.map((option) => (
<Checkbox option={option} key={option} onChange={handleChange} />
))}
</MaterialSelect>
</FormControl>
</>
);
}
With that structure, you can receive checked value on App.js and filter data.
could you please tell me how to set value in dropdown in react js ?
I am getting dropdown data after few seconds 3000 and then I need to set value on dropdown
const App = ({ children }) => {
const val = "ax";
const [state, setState] = useState([]);
setTimeout(() => {
setState(countryOptions);
}, 2000);
return (
<Container style={{ margin: 20 }}>
<Example countryOptions={state} />
</Container>
);
};
https://codesandbox.io/s/semantic-ui-example-utev4
expected output
Aland Islands should be selected.
{ key: "ax", value: "ax", text: "Aland Islands" },
as after three second I want to select this element
const val = "ax";
As stavros answer suggested; it may be better to keep state in App component and pass the setVal to the dropdown:
App:
const App = ({ children }) => {
const [state, setState] = useState([]);
//added val and setVal in App state
const [val,setVal]=useState('ax');
setTimeout(() => {
setState(countryOptions);
}, 2000);
return (
<Container style={{ margin: 20 }}>
//pass val and setVal so dropdown can set val on change
<Example countryOptions={state} val={val} onChange={setVal}/>
</Container>
);
};
Dropdown:
const DropdownExampleClearableMultiple = ({ countryOptions,val,onChange }) => (
<Dropdown
clearable
fluid
search
closeOnChange
selection
options={countryOptions}
//set value to passed in val
value={val}
//use setVal that was passed in as onChange
onChange={(_,i)=>onChange(i.value)}
placeholder="Select Country"
/>
);
You should update your question because only after visiting the codesandbox was I able to get enough info for an answer..
In your index.js you should update setState(countryOptions) to :
setState({countryOptions:countryOptions},()=>setState({val:"ax"})
Then line 39 to :
<Example countryOptions={state.countryOptions:countryOptions} val={state.val} />
Then in your example.js update const DropdownExampleClearableMultiple to:
const DropdownExampleClearableMultiple = ({ countryOptions, val }) => (
<Dropdown
clearable
fluid
search
closeOnChange
selection
options={countryOptions}
placeholder="Select Country"
value={val}
/>
);
Use the "value" prop.
// index.js
const App = ({ children }) => {
const val = "ax";
const [state, setState] = useState([]);
setTimeout(() => {
setState(countryOptions);
}, 2000);
return (
<Container style={{ margin: 20 }}>
<Example countryOptions={state} value={val} />
</Container>
);
};
// example.js
const DropdownExampleClearableMultiple = ({ countryOptions, value }) => (
<Dropdown
clearable
fluid
search
closeOnChange
selection
value={value}
options={countryOptions}
placeholder="Select Country"
/>
);
You can pass value to value property of Dropdown. Use state property for selected value.
Make sure that you change/update state on onchange event.