I have a react select component that isn't recognizing the default value option.
The code looks like this:
renderPlans(){
if(this.props.plans){
let list = this.props.plans.map(item=>{
return ({label:item.description, value:item.id})
});
return(
<Select
name= "tile-plans"
value= {this.state.selected}
classNamePrefix='react-select-container'
options={list}
defaultValue={list[0]}
onChange={(e) => { e ? this.setState({selected: e.value}) : this.setState({selected: ''}) }}
/>
)
}
}
from everything I can find on its docs this is the format to give it. Basically I just want the first option to always be the default choice as there will be times when there is only 1 option and it doesn't make sense for someone to need to select a drop down. I also have a graph that's being loaded underneath so if an option is selected the graph won't load.
This isn't a duplicate question as I know you can do it like this:
value= {this.state.selected ? this.state.selected:list[0].label}
but it's not working. The input remains blank on load.
The documentation states that "If you don't provide these props, you can set the initial value of the state they control", referencing among others the value prop you provide.
You can set the selected to the first element in list when the component is created instead.
Example
class App extends React.Component {
state = {
selected: this.props.list[0]
};
handleChange = selected => {
this.setState({ selected });
};
render() {
const { selected } = this.state;
return (
<Select
value={selected}
onChange={this.handleChange}
options={this.props.list}
/>
);
}
}
ReactDOM.render(
<App
list={[
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" }
]}
/>,
document.getElementById("root")
);
Related
I have a usecase where I need to work with multiple checkboxes in a list
Only one check box can be selected at a time
Eg. If I toogle on Checkbox 1 and then click on Checkbox 2 - then I need to toogle off Checkbox 1 as I toogle on Checkbox 2
Please check the code at CODESANDBOX
I have added the parent component for context, I hope this will be fixable.
Please help me fix this
Thank you
Okay, so this is kinda hard to do without good demo code all in one spot or a codesandbox, so here's a much smaller demo that works using much smaller and simpler objects. If you check a box, the rest become unchecked. You can see how I bind the state in the child component, and the parent updates the state accordingly. You can pop this into a code sandbox with no dependencies other than react and typescript.
Full demo: https://codesandbox.io/s/checkbox-exclusive-lf2vz9
import { useState } from "react";
import "./styles.css";
const AnimalArray = [
{
name: "Bear",
selected: false
},
{
name: "Cat",
selected: false
},
{
name: "Dog",
selected: true
}
];
type Animal = {
name: string;
selected: boolean;
};
type Animals = {
animalList: Array<Animal>;
};
type AnimalProps = {
animal: Animal;
onSelectedChanged: (animal: Animal) => void;
};
const AnimalComponent = (props: AnimalProps) => {
return (
<div>
<input type="text" value={props.animal.name} readOnly={true} />
<input
type="checkbox"
checked={props.animal.selected}
onChange={(el) => {
props.onSelectedChanged({
name: props.animal.name,
selected: el.target.checked
});
}}
/>
</div>
);
};
const AnimalListComponent = (props: Animals) => {
// this could be further pushed up state instead of (or in addition to) being handled here
const [animals, setAnimals] = useState<Animal[]>(props.animalList);
const onChange = (currentAnimal: Animal) => {
setAnimals((currentState) =>
currentState.map((i: Animal) =>
i.name === currentAnimal.name
? {
...i,
selected: currentAnimal.selected
}
: {
...i,
selected: currentAnimal.selected ? false : i.selected
}
)
);
};
return (
<>
<p>list</p>
{animals.map((animal, index) => (
<AnimalComponent
key={index}
onSelectedChanged={onChange}
animal={animal}
/>
))}
</>
);
};
export default function App() {
const [animals] = useState<Animals>({ animalList: AnimalArray });
return (
<div className="App">
<AnimalListComponent animalList={animals.animalList} />
</div>
);
}
In ES6, ComputedPropertyName allows us to do things like use a variable as a key, which in turn means we can set state dynamically. However, if you look around at examples of setting state dynamically, they tend to all have one thing in common -- the state key's name is hardcoded. As an example:
class Input extends React.Component {
state = { state1: "" };
handleChange = event => {
const {
target: { name, value }
} = event;
this.setState({
[name]: value
});
};
render() {
return (
<div>
<label>
<input
type="text"
name="state1"
value="new value"
onChange={this.handleChange}
/>
</label>
</div>
);
}
}
This works because we have a state key called "state1", as seen in the line state = { state1: "" };, and we are hardcoding name in the input field to be that state key, as seen in the line name="state1".
I do not like this solution, because it means I now have to keep track of state.state1" in more than one location. If I were to refactorstate.state1to instead bestate.state2, I would have to go findname="state1"1 and update that to read name="state2". Instead of worry about that, I am wondering if there is a way to set state dynamically without hardcoding this state key. That is, I'm looking to change
<input
type="text"
name="state1"
value="new value"
onChange={this.handleChange}
/>
Into something like:
<input
type="text"
name={this.state.state1.keyname}
value="new value"
onChange={this.handleChange}
/>
Obviously the above doesn't work because keyname is undefined, but the intention here is that name can take on the value of "state1" without me having to hardcode it. How can this be achieved?
You can have an array with objects with keys type and name which you can use to set the initial state and render the inputs dynamically. This way you'll only have to change the value once in the array. You can do something like this.
Here is a codesandbox
import React from "react";
import ReactDOM from "react-dom";
class App extends React.Component {
constructor() {
super();
this.arr = [
{ type: "text", name: "state1" },
{ type: "password", name: "state2" }
];
// set the state keys dynamically from this.arr
this.state = this.arr.reduce((agg, item) => {
agg[item.name] = "";
return agg;
}, {});
}
handleChange = event => {
const {
target: { name, value }
} = event;
this.setState(
{
[name]: value
}
);
};
renderInputs = () => {
return this.arr.map((item, i) => (
<div key={i}>
<label>
<input
type={item.type}
name={item.name}
value={this.state[item.name]}
onChange={this.handleChange}
/>
</label>
</div>
));
};
render() {
const inputs = this.renderInputs();
return <div>{inputs}</div>;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Hope this helps !
There is the new useReducer() that comes with hooks and context. Check this out i think that is the best pattern to solve your issue. https://reactjs.org/docs/hooks-reference.html.
Through the official documentation of antd we can know how to use the checkbox to complete the switch between multiple selection and single selection.
https://ant.design/components/checkbox/
My question is, if my checkbox data comes from a backend service, how should I maintain my data? It's accurate to say when I save the data in state of class so that the changes to the UI can be affected by changes in the data like the official documentation.
Now I try to traverse the back-end data when rendering the Dom, the following example code:
import { Checkbox } from 'antd';
const CheckboxGroup = Checkbox.Group;
class App extends React.Component {
state = {
indeterminate: true,
checkAll: false,
};
render() {
return (
<div>
<div style={{ borderBottom: '1px solid #E9E9E9' }}>
<Checkbox
indeterminate={this.state.indeterminate}
onChange={this.onCheckAllChange}
checked={this.state.checkAll}
>
Check all
</Checkbox>
</div>
<br />
{
this.renderDomFunction(data)
}
</div>
);
}
// data is from back-end server
renderDomFunction = (data) => {
let plainOptions = []
let defaultCheckedList = []
let dom
data.map(item => {
plainOptions.push(
{
label: <div>this is Orange</div>,
value: 'Orange',
disabled: false
},
{
label: <div>this is Apple</div>,
value: 'Apple',
disabled: false
},
)
defaultCheckedList.push('Orange','Apple')
})
return (
dom = <li>
<CheckboxGroup
options={plainOptions}
value={defaultCheckedList}
onChange={this.onChange}
/>
</li>
)
}
onChange = () => {
// code...
// I can't change the state of the checkbox by changing the data now, because isn't maintained in the state of Class.
}
}
ReactDOM.render(<App />, mountNode);
I also tried to put the setstate() function into the renderDomFunction but this would cause an infinite loop.
Thank you!
Need some help with dropdowns. I can't seem to figure out a way to pass in the ID specific to that particular dropdown <option> so that when the user makes a selection that value can be used (for example to setState). The usual case is to read the value of the dropdown itself, but in my case I don't want the displayed value but another value that is 'behind the scenes' or hidden.
Please note I've already researched about controlled components in react for forms etc, this issue is a bit different and my attempts to find an answer have come up empty.
I'm using react and javascript but I think if you know a little about javascript you may be able to help.
The dropdowns are set up like shown below where on changing the dropdown selection it runs the function handleStatusChange.
<select
id="inputIdentity"
className="select-form"
onChange={this.handleStatusChange}
>
<option value="" hidden>
please choose
</option>
<option>status1</option>
<option>status2</option>
<option>status3</option>
</select>
In actual practice the options are mapped out from data fetched via an api as they can be altered in the backend:
<select
id="inputIdentity"
className="form-control content-input"
onChange={this.handleStatusChange}
>
<option value="" hidden>
Please Choose
</option>
{statusData ? (
statusData.map((status) => (
<option>{status.title}</option>
))
) : (
<option>Loading...</option>
)}
</select>
this.handleStatusChange checks the value of the event (which is the act of changing the selection in the dropdown, the value is accessed with: e.target.value) to read the value inside the <option> that was chosen... The method for reading the chosen dropdown value is something like:
handleStatusChange = (e) => {
this.setState({
// set it to state
status: e.target.value
});
};
I
This is the standard way to do it.
Now we get back to the question - is there a way to read a value from each dropdown that is NOT shown (read from e.target.value) instead, ie. if they each had an ID or something, how do I pass that in so that e.target.value would be able to access this id.
If you take a look at the version where I map out (or loop out if you aren't familiar with the map function) the <option> tags and in each iteration I pass in the title with {status.title}. I can access the id with status.id but the onChange handler is in the <select> tag and can't be within the map / loop, so to handle it by passing in the id into the onChange handler is not possible either.
What would be the correct way for the onChange handler to have access to that specific value which is not displayed between <option> tags?
You can keep the options in your component state and use the array method find to find the option that corresponds to the selected option and use that.
Example
class App extends React.Component {
state = {
options: [
{ value: "status1", label: "Status 1", secretValue: "foo" },
{ value: "status2", label: "Status 2", secretValue: "bar" },
{ value: "status3", label: "Status 3", secretValue: "baz" }
],
selectedOption: ""
};
handleStatusChange = e => {
const { value } = e.target;
this.setState(prevState => {
const { secretValue } = prevState.options.find(
option => option.value === value
);
console.log(secretValue);
return { selectedOption: value };
});
};
render() {
const { options, selectedOption } = this.state;
return (
<select value={selectedOption} onChange={this.handleStatusChange}>
<option value="" hidden>
please choose
</option>
{options.map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
you can pass that value to the loop showing the Options.
{statusData ? (
statusData.map((status) => (
<option value={status.value}>{status.title}</option>
))
) : (
<option>Loading...</option>
)}
and in order to do that you have to modify your array statusData.
hiddenData = ['a', 'b', 'c'];
statusData.map((s, index) => {
s.value = hiddenData[index]
return s;
});
is there a way to read a value from each dropdown that is NOT shown (read from e.target.value) instead, ie. if they each had an ID or something, how do I pass that in so that e.target.value would be able to access this id.
e.target is a reference to the HTMLSelectElement where the change occurred. You can find the option with the matching value in its options list, and then use that HTMLOptionElement's properties, like this:
handleStatusChange({target}) {
const value = target.value;
const optionElement = Array.from(target.options).find(opt => opt.value === value);
// If found, use information from `optionElement` to find the
// entry in `statusData` in a state change, e.g.:
if (optionElement) {
const id = optionElement && optionElement.id;
if (id) {
this.setState(state => {
const entry = state.statusData.find(e => e.id === id);
if (entry) {
// Use `entry`'s information
}
}
}
}
}
React example, using a details property on the entries in statusData:
class Example extends React.Component {
constructor(...args) {
super(...args);
this.handleStatusChange = this.handleStatusChange.bind(this);
this.state = {
detail: "",
statusData: [
{id: "one", title: "One", detail: "Details for one"},
{id: "two", title: "Two", detail: "Details for two"},
{id: "three", title: "Three", detail: "Details for three"}
]
};
}
handleStatusChange({target}) {
const value = target.value;
const optionElement = Array.from(target.options).find(opt => opt.value === value);
const id = optionElement && optionElement.id;
if (id) {
this.setState(state => {
const entry = state.statusData.find(e => e.id === id);
if (entry) {
return {
detail: entry.detail
}
}
});
}
}
render() {
const {statusData, detail} = this.state;
return (
<div>
<select
id="inputIdentity"
className="form-control content-input"
onChange={this.handleStatusChange}
>
<option value="" hidden>
Please Choose
</option>
{statusData ? (
statusData.map((status) => (
<option id={status.id}>{status.title}</option>
))
) : (
<option>Loading...</option>
)}
</select>
<div>Detail: {detail}</div>
</div>
);
}
}
ReactDOM.render(
<Example />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Imagine a simple React component with <select> element that allows to choose a city based on country. For example
<MyCitySelectComponent
country={ 'France' }
city={ 'Paris' }
onChange={ someFunction }
/>
When mounted, it should load list of available Cities (based on Country) and render <select>.
When city property is changed - it should modify <select> input value and trigger onChange event.
When country property is changed (from parent component) - it should reload list of available cities from a remote server and trigger the same onChange event.
I managed to implement first two, here is simplified code:
class MyCitySelectComponent extends Component {
constructor(props) {
super(...props);
this.state = {
cities: null,
city: props.city,
country: props.country
};
}
onCityChange( e ) {
this.setState({
city: e.target.value
});
this.props.onChange( e.target.value );
}
loadCities() {
fetch({
path: '/get/cities?country=' + this.state.country,
}).then( cities => {
this.setState({
cities: cities
});
});
}
componentDidMount() {
this.loadCities();
}
render() {
if ( !this.state.cities ) {
// not loaded yet
return null;
}
return (
<select>
{ this.state.cities.map( ( name, index ) =>
<option
value={ name }
onChange={ this.onCityChange }
selected={ name === this.state.city }
/>
) }
</select>
)
}
}
But I'm having trouble reloading cities when country is changed dynamically from parent component. I tried using shouldComponentUpdate, but all I get is infinite loops.
Is there any pattern for such type of component?
Thank you.
Fetching new data based on prop changes should be handled in componentDidUpdate or getDerivedStateFromProps. Have a look at the docs for an example: https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#fetching-external-data-when-props-change
Note that componentWillReceiveProps is deprecated!