I have three different selections and I want to store each selected and current option its own state. I also want when the user changes option to store that on the state.
Here is my states:
this.state = {
header: [
'genre',
'sold',
'income'
],
chartData: [
'Line',
'Bar',
'AreaChart',
'PieChart',
'ScatterChart'
],
axisX: '',
axisY: '',
selectedLine: ''
}
Here is my "handleChart" function. Basically what I am trying to do here is, when the component loads I want to store whatever current options on the state and when the user changes or selects new option, I want to store that on the state too.
handleChart = (e) => {
e.preventDefault();
const value = e.target.value;
if(value === 'line'){
// First selection: contains >>> different chart names
this.setState({selectedLine: value})
}else if(value === 'genre'){
// Second selection: contains >>> different headers
this.setState({axisX: value})
}else if(value === 'income'){
// Third selection: contains >>> different headers
this.setState({axisY: value)
}
}
My App.js
<div className="App">
<div style={{display: 'inline-block'}}>
<span>Chart type: </span>
<select onChange={this.handleChart} style={{marginRight: '15px'}}>
{this.state.chartData.map((k, v) => (
<option name="line" key={k} value="line">{k}</option>
))}
</select>
<span>x-axis: </span>
<select onChange={this.handleChart} style={{marginRight: '15px'}}>
{this.state.header.map((k, v) => (
<option name="xaxis" key={k} value={k}>{k}</option>
))}
</select>
<span>y-axis: </span>
<select onChange={this.handleChart} style={{marginRight: '15px'}}>
{this.state.header.map((k, v) => (
<option name="yaxis" key={k} value={k}>{k}</option>
))}
</select>
</div>
</div>
It looks like you already have a way to update state based on changing the value of an input, however your current approach can be improved to be more robust. You can give each input a name attribute and then have the name correspond to the variable name within state so you don't need a bunch of if/else if blocks.
To have inputs take values from state, set the value attribute on the input/select/etc (rather than on option) equal to the value from state that you want to use.
Here is an updated version of handleChart:
handleChart = event => {
event.preventDefault();
const { name, value } = event.target;
this.setState({
[name]: value;
});
}
And updated App.js:
render() {
const { axisX, axisY, selectedLine } = this.state;
return (
<div className="App">
<div style={{display: 'inline-block'}}>
<span>Chart type: </span>
<select name="selectedLine" value={ selectedLine } onChange={this.handleChart} style={{marginRight: '15px'}}>
{this.state.chartData.map((k, v) => (
<option name="line" key={k} value={v}>{v}</option>
))}
</select>
<span>x-axis: </span>
<select name="axisX" value={ axisX } onChange={this.handleChart} style={{marginRight: '15px'}}>
{this.state.header.map((k, v) => (
<option name="xaxis" key={k} value={v}>{v}</option>
))}
</select>
<span>y-axis: </span>
<select name="axisY" value={ axisY } onChange={this.handleChart} style={{marginRight: '15px'}}>
{this.state.header.map((k, v) => (
<option name="yaxis" key={k} value={v}>{v}</option>
))}
</select>
</div>
</div>
)
}
Related
I'm trying to update the values of a multidimensional array with the values of a select input inside of a forEach loop. It's one array that holds two inner arrays. Every time I change the values with the select input, both inner arrays get updated. How can I get the inner arrays to update individually?
codesandbox - https://codesandbox.io/s/multidimensional-array-3v6pmi
Update the state fxChoices considering it as a multi-dimentional array. Please refer the code-sandbox here.
Try to set the state as the value of each drop-down. Don't use the drop-down only to change the state, but use it to view the actual state that you've set.
Organize your component like below. Now it's just a matter of changing NUMER_OF_INPUTS as many inputs you want.
const NUMER_OF_INPUTS = 2;
function App() {
const [fxChoices, setFxChoices] = React.useState(
Array.from({ length: NUMER_OF_INPUTS }).map(() => [[]])
);
console.log("fxChoices", fxChoices);
const onChangeHandler = (compIndex, optionIndex, option) => {
setFxChoices((prevState) =>
prevState.map((item, itemIndex) => {
if (itemIndex === compIndex) {
const copyItem = [...item];
copyItem[optionIndex] = option;
return copyItem;
}
return item;
})
);
};
return (
<div className="App">
{Array.from({ length: NUMER_OF_INPUTS }).map((_, j) => {
return (
<div key={j}>
<p>bus{j + 1}</p>
<SelectComponent compIndex={j} onChangeHandler={onChangeHandler} />
</div>
);
})}
</div>
);
}
const SelectComponent = ({ onChangeHandler, compIndex }) => {
return Array.from({ length: 2 }).map((_, i) => (
<div key={i} style={{ display: "flex", flexDirection: "column" }}>
<select
onChange={(e) => {
onChangeHandler(compIndex, i, e.target.value);
}}
className="effect-select"
>
<option value={`bs${i + 1}-fx${i + 1}`}>{`FX${i + 1}`}</option>
<option value="reverb">Reverb</option>
<option value="delay">Delay</option>
<option value="chorus">Chorus</option>
<option value="chebyshev">Chebyshev</option>
<option value="pitch-shift">PitchShift</option>
<option value="compressor">Compressor</option>
</select>
</div>
));
};
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
NOTE: Try to think like building boxes when designing components.
Maybe it is easier to map you multidimensionnal array to access the indexes easily like below
import { useState } from "react";
import "./styles.css";
export default function App() {
const Options = () => {
const [fxChoices, setFxChoices] = useState([...Array(2)].map(() => [...Array(2)].map(() => null)));
console.log(fxChoices)
return (<>
{fxChoices.map((item, i) => (
<div key={i}>
<p>bus{i + 1}</p>
<div style={{ display: "flex", flexDirection: "column" }}>
{item.map((_, index) => <select
onChange={(e) => {
fxChoices[i][index] = e.target.value
setFxChoices(fxChoices);
console.log("fxChoices", fxChoices);
}}
className="effect-select"
>
<option value={`bs${i + 1}-fx${i + 1}`}>{`FX${i + 1}`}</option>
<option value="reverb">Reverb</option>
<option value="delay">Delay</option>
<option value="chorus">Chorus</option>
<option value="chebyshev">Chebyshev</option>
<option value="pitch-shift">PitchShift</option>
<option value="compressor">Compressor</option>
</select>)}
</div>
</div>
))
}
</>
);
};
return (
<div className="App">
<Options />
</div>
);
}
So my issue is that I have a drop-down menu and the items are a bit strange. The first row looks blank and the data that I have looks like this.
I know the first user has a username: null and that is probably why the blank is showing but in the frontend I want to change the username of user of index 0 to always say "Myself" whether it is null or not. How Do I do modify its value?
This is how the drop-down looks like
async componentDidMount() {
const ep = `${process.env.REACT_APP_API}/partners/roles`;
const users = await axios.get(ep);
if (users.data.success) {
this.setState({
users: users.data.data.users.filter(f => !f.isArchived)
});
}
console.log("Users", users)
if (this.props.item) {
this.setState({
id: this.props.item.id,
description: this.props.item.description,
due: this.props.item.due,
priority: this.props.item.priority,
assigned: this.props.item.assigned
});
}
}
This is where the drop-down items are being generated
<select
name="assigned"
id="assignedto"
type="text"
className="form-control"
style={{ width: "70%" }}
onChange={e => {
this.setState({
assigned: e.target.value
});
}}
>
<option value="">Myself</option>
{this.state.users.map(e => (
<option value={e.id} key={e.id}>
{e.username}
</option>
))}
</select>
Maybe use the index?
<select
name="assigned"
id="assignedto"
type="text"
className="form-control"
style={{ width: "70%" }}
onChange={e => {
this.setState({
assigned: e.target.value
});
}}
>
<option value="">Myself</option>
{this.state.users.map(e => (
<option value={e.id} key={e.id} index>
index == 0 ? 'Myself' : {e.username}
</option>
))}
</select>
You can add a check while rendering like this:
<option value={e.id} key={e.id}> {e.username ? e.username : 'Myself'} </option>
Usually the right approach is to clean your data when you're setting the state:
users.forEach((u => { if (!u.name) { u.name = 'Myself'; } }));
I have a select such as :
<Select id='1' list={[...this.state.dictionnary]}/>
where this.state.dictionnary is like this :
state = {
dictionnary: [
{value: "a", name: "ab"},
]}
And the component select is like this :
class Select extends Component {
handleChange()
{
// I would like to show 1, a and ab
// 1 is the id of the select
// a and ab are value and name of
};
render() {
return (
<select onChange={this.handleChange} className="custom-select">{this.props.list.map(option => (
<option key={option.value} value={option.value}>{option.name}</option>))}
</select>
)
}
}
export default Select;
I would like to show some informations using the handleChange() function like the id, name and value.
Could you help me please ?
Thank you very much
You should change the option so the value has the entire object to get it later in the handleChange. Also, to access the same this with the props in the handleChange method you need to use an arrow function:
<select onChange={(e) => this.handleChange} className="custom-select">
{this.props.list.map((option) => (
<option key={option.value} value={option}>
{option.name}
</option>
))}
</select>
By default, the onChange handler gets the event param so you can get the target and the props:
handleChange(event) {
const { value, name } = event.target.value;
console.log(this.props.id, value, name);
}
Adding to #Alavaro answer, you need to split after getting the value to remote ,
handleChange = e => {
const [value, name] = e.target.value.split(',')
console.log({ id: this.props.id, value, name})
}
render() {
return (
<select onChange={this.handleChange} className="custom-select">
{this.props.list.map((option, index) => (
<option key={option.value} value={[option.value, option.name]} >
{ option.name }
</option>
))}
</select>
);
}
}
<Form>
<Form.List name="projects">
{fields =>
...
fields.map(field => <Form.Item name={[field.key, "type"]} hidden={true} initialValue={type} />)
...
}
</Form.List>
</Form>
Whenever type changes, I want to set Item value dynamically.
I think I can get form instance using the useForm and use the setFieldsValue like below.
form = useForm()
onChange(type) {
form.setFieldsValue(/* values here */)
}
But I am not sure how to use the form.setFieldsValue to refer item inside Form.List.
Who can give me solution?
The Antd form list creates an array of item objects. Then it is necessary to locate the position of the object within the array that contains the item that will have the value changed.
Return all values for form items:
const fields = form.getFieldsValue()
Retrieve the array containing the form list objects:
const {projects} = fields
Pass the object's position within the array and change the item to the new value:
Object.assign(projects[key], {type: value})
Then pass the new array value to the form:
form.setFieldsValue({projects})
Here is a complete example:
import React from 'react';
import { Form, Select, Button } from 'antd'
const { Option } = Select
export default function FormProject() {
const [ form ] = Form.useForm()
const onChange = (value, key) => {
const fields = form.getFieldsValue()
const { projects } = fields
Object.assign(projects[key], { type: value })
form.setFieldsValue({ projects })
}
return (
<Form
form={form}
initialValue={{type: "type_1"}}
>
<Form.List name="projects">
{(fields, { add, remove }) => (
<div>
<Form.Item>
<Button type="dashed" onClick={add} >
Add
</Button>
</Form.Item>
{fields.map(field => (
<div>
<Form.Item name={[field.key, "type"]} hidden={true} />
<Form.Item noStyle name={[field.name, 'id']}>
<Button type="dashed" onClick={remove} >
Remove
</Button>
</Form.Item>
<Form.Item
{...field}
name={[field.name, 'type_project']}
label="Tipo"
fieldKey={[field.fieldKey, 'type_project']}
>
<Select onChange={e => onChange(e, field.key)}>
<Option value="type_1">Type 1</Option>
<Option value="type_2">Type 2</Option>
<Option value="type_1">Type 2</Option>
</Select>
</Form.Item>
</div>
))}
</div>
)}
</Form.List>
</Form>
);
}
One of the ways to manage those nested values is to get the whole value and change the attribite you need and then pass it to your form:
const value = form.getFieldValue(field.key)
form.setFieldsValue({[field.key]: {...value, ['type']: your_new_value}})
I made it without mutation. Because mutation did not refresh updated field.
just an idea:
const onChange = (value, originalProject) => {
const projects = form.getFieldsValue('projects')
const updatedProjects = projects.map(project => {
if (project.id === originalProject.id) {
return {
...project,
updateKey: value
}
}
return project;
})
form.setFieldsValue({ projects: updatedProjects })
}
This worked for me.
const data = form.getFieldValue('projects');
data[key] = { ...data[key], [property name]: 'new property value' };
form.setFieldValue('projects', data);
I hope this helps you or anyone reading this. Happy coding!
I have 2 <Select>'s. The values in the second are dependant on the selection made on the first. When I change the selected item in the first, the available options on the second update. But if I already have a selection made on the second, that option remains selected even if it isn't supposed to be available based on a change to the first select.
How can I reset the second select to have nothing selected when a change is made to the first select?
First Select:
<FormItem {...formTailLayout}>
<FormTitle>Operation</FormTitle>
{getFieldDecorator('Operation', {
rules: [
{
required: true
}
]
})(
<Select
showSearch
placeholder="Select an option"
onChange={this.handleOperationChange}
>
{operations.map(operation => (
<Option value={operation.operation_id}>
{operation.operation_name}
</Option>
))}
</Select>
)}
</FormItem>
Second Select:
<FormItem {...formTailLayout}>
<FormTitle>Metric</FormTitle>
{getFieldDecorator('Metric', {
rules: [
{
required: true
}
]
})(
<Select
showSearch
placeholder="Select an operation first"
onChange={this.handleMetricChange}
>
{matrics
.filter(
metric => metric.operation_fk === operation_fk
)
.map(metric => (
<Option value={metric.metric_name}>
{metric.metric_name}
</Option>
))}
</Select>
)}
</FormItem>
You need to take a look at Coordinated Controls example mentioned on ant-design page. You can simply use setFieldsValue in your first onChange method to set the value of second select field.
handleOperationChange = () => {
this.props.form.setFieldsValue({
Metric: undefined
})
}
I have created a sandbox demo.
In antd, In Class Component, Using Ref, Clear or reset select list value or form item value
add formRef = React.createRef(); just below class component, like:
export default class TestClass extends Component { formRef = React.createRef(); }
add ref={this.formRef} in <Form /> component like this <Form ref={this.formRef}/>
add this line on btn click or in any function you want
this.formRef.current.setFieldsValue({ network: undefined });
Here network in above step is the name of form item
<Form.Item name="network" label="Network"></Form.Item>
Inside handleOperationChange method use resetfileds, this gonna reset your second select box.
this.props.form.resetFields('Metric');
<Select
className="mt-3"
style={{ width: "100%" }}
placeholder="Select your Sub Category"
onChange={handleChangeSubCategory}
disabled={categoryGroup.category === null}
value={categoryGroup.subcategory || undefined}
>
{subCategory.map(item => (
<Option key={item} value={item} label={item}>
<div>
<Avatar style={{ background: "#10899e" }}>
{item[0]}
</Avatar>{" "}
{item}
</div>
</Option>
))}
</Select>
If functional component is used, then we can use Form:
const [form] = Form.useForm()
and it is possible to clear value like this:
const someMethodToClearCurrentSelection = () => {
// ... the code is omitted for the brevity
form.setFieldsValue({ fooProduct: undefined })
}
and our control looks like this:
<Form form={form} name="basic" onFinish={onFinish} layout="vertical">
<Form.Item
key="fooProduct"
name="fooProduct"
label="Some product"
className={s.fooContainer}
rules={[
{
required: true,
message: `Field 'Some produc' is required`,
},
]}
>
<Select
showSearch
optionFilterProp="children"
filterOption={(input, option) =>
option?.children.toLowerCase().includes(input.toLowerCase())
}
allowClear
onChange={handleMarkClick}
>
{products.map((option) => (
<Option key={option.id} value={option.id}>
{option.name}
</Option>
))}
</Select>
</Form.Item>
Read more in antd docs here and examples while migrating from v3 to v4
You can make use of the useEffect() hook.
Define a useForm() custom hook fir your form.
Example: const [form] = Form.useForm();`
Wrap your 1st and 2nd select boxes inside the form and use <Form.Item> to define the input on the form. And whenever the first select's input is changed, clear the 2nd select values in useEffect() hook.
Example:
useEffect(() => {
form.setFieldsValue({
marketplace: [] //2nd select name
});
}, [selectedRegion]); //first select's value
This work perfect for me.