I am trying to reset Core UI ReactJS CMultiSelect component with Reset button. I have used setState method to reset the value. As soon as I click on the reset button, the state value changes, but immediately onChange method of CMultiSelect is called and the existing value is retained.
Below is the code snippet I'm trying.
import React from 'react'
import { CRow, CMultiSelect, CFormInput, CButton } from '#coreui/react-pro'
class TestForm extends React.Component<{}, { textVal: string; dropdownVal: string[] }> {
constructor(props: any) {
super(props)
this.state = { textVal: '123', dropdownVal: [] }
}
setTextVal(newVal: string) {
this.setState({ textVal: newVal })
}
setTest(newVal: string[]) {
this.setState({ dropdownVal: newVal })
}
render() {
return (
<div className="row m-5">
<div className="col-sm-6">
<CFormInput
type="text"
value={this.state.textVal}
onChange={(evt) => {
this.setTextVal(evt.target.value)
}}
></CFormInput>
</div>
<div className="col-sm-6">
<CMultiSelect
multiple={false}
options={[
{
value: '1',
text: '1',
selected: this.state.dropdownVal.indexOf('1') >= 0,
},
{
value: '2',
text: '2',
selected: this.state.dropdownVal.indexOf('2') >= 0,
},
{
value: '3',
text: '3',
selected: this.state.dropdownVal.indexOf('3') >= 0,
},
]}
onChange={(val) => {
console.log('on change called', val)
this.setTest(
val.map((x) => {
return x.value.toString()
}),
)
}}
></CMultiSelect>
</div>
<div className="col-sm-6">
<CButton
className="mt-3"
type="reset"
value="Reset"
onClick={() => {
this.setTest([])
this.setTextVal('')
}}
>
Reset
</CButton>
</div>
</div>
)
}
}
export default TestForm
When I hit the reset button, value of the text field resets, but not the multi-select dropdown.
try calling the reset method on the component instance. You can do this by saving a reference to the CMultiSelect instance in your component's state, and then calling the reset method on that instance in your reset handler.
class MyComponent extends React.Component {
state = {
select: null,
};
handleReset = () => {
this.state.select.reset();
}
render() {
return (
<div>
<CMultiSelect
ref={(select) => this.setState({ select })}
onChange={this.handleChange}
/>
<button onClick={this.handleReset}>Reset</button>
</div>
);
}
}
Related
I'm writing a simple to do list app, but the functional component that maps the to-do items in the array is not running again after rendering the initial items and I add another one and I don't' know why.
I can see in the console that the state is being updated with a new item, so I'm just stuck wondering why it's not displaying on the page when I add a new item.
import React from 'react';
class App extends React.Component {
state = { items: [
{
id: 1,
item: 'code another react app'
},
{
id: 2,
item: 'eat some cheetos'
}
],
inputVal: ''
}
onFormSubmit = (e) => {
e.preventDefault();
let newId = Math.random();
let newItem = this.state.inputVal;
this.setState({
items: [
...this.state.items,
{id: newId, item: newItem }
],
inputVal: ''
})
}
toDoList = this.state.items.map(item => {
console.log(item)
return (
<div key={item.id}>
<li>{item.item}</li>
</div>
)
})
render() {
console.log(this.state)
return (
<div>
<form onSubmit={this.onFormSubmit}>
<input
placeholder="add a to do item"
value={this.state.inputVal}
type="text"
onChange={e => this.setState({ inputVal: e.target.value })}
/>
</form>
{this.toDoList}
</div>
);
}
}
export default App;
The toDoList variable is set only one time.
you have to use in render method.
render() {
const toDoList = this.state.items.map(item => {
console.log(item)
return (
<div key={item.id}>
<li>{item.item}</li>
</div>
)
})
return (
<div>
<form onSubmit={this.onFormSubmit}>
<input
placeholder="add a to do item"
value={this.state.inputVal}
type="text"
onChange={e => this.setState({ inputVal: e.target.value })}
/>
</form>
{toDoList}
</div>
);
}
I made a PoC to see how to handle my change detection on a dynamic list of checkboxes (note, i do not know beforehand how many checkboxes i have.) I created a n ES6 map (Dictionary) that tracks the checked state of each individual checkbox. but for some reason I get the following error:
A component is changing an uncontrolled input of type checkbox to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component
Usually when my number of form input fields is known i track them via the state, but how would one handle this case. The logic works fine, but I need to get rid of the error.
My app code:
import React, { Component } from "react";
import Checkbox from "./checkbox";
class App extends Component {
constructor(props) {
super(props);
this.state = {
checkedItems: new Map()
};
this.handleChange = this.handleChange.bind(this);
}
handleChange = e => {
const item = e.target.name;
const isChecked = e.target.checked;
this.setState(prevState => ({
checkedItems: prevState.checkedItems.set(item, isChecked)
}));
};
deleteCheckboxState = (name, checked) => {
const updateChecked = typeof checked === "undefined" ? true : false;
this.setState(prevState => prevState.checkedItems.set(name, updateChecked));
};
clearAllCheckboxes = () => {
const clearCheckedItems = new Map();
this.setState({ checkedItems: clearCheckedItems });
};
render() {
const checkboxes = [
{
name: "check-box-1",
key: "checkBox1",
label: "Check Box 1"
},
{
name: "check-box-2",
key: "checkBox2",
label: "Check Box 2"
},
{
name: "check-box-3",
key: "checkBox3",
label: "Check Box 3"
},
{
name: "check-box-4",
key: "checkBox4",
label: "Check Box 4"
}
];
const checkboxesToRender = checkboxes.map(item => {
return (
<label key={item.key}>
{item.name}
<Checkbox
name={item.name}
checked={this.state.checkedItems.get(item.name)}
onChange={this.handleChange}
type="checkbox"
/>
</label>
);
});
const checkboxesDeleteHandlers = checkboxes.map(item => {
return (
<span
key={item.name}
onClick={() =>
this.deleteCheckboxState(
item.name,
this.state.checkedItems.get(item.name)
)
}
>
{item.name}
</span>
);
});
return (
<div className="App">
{checkboxesToRender}
<br /> {checkboxesDeleteHandlers}
<p onClick={this.clearAllCheckboxes}>clear all</p>
</div>
);
}
}
export default App;
The checkbox reusable component:
import React from "react";
class Checkbox extends React.Component {
render() {
return (
<input
type={this.props.type}
name={this.props.name}
checked={this.props.checked}
onChange={this.props.onChange}
/>
);
}
}
export default Checkbox;
The problem is that the checked state is initially undefined, which is a falsy value, but interpreted as not provided.
So you can simply ensure that the falsy state will actually be false by using !!.
So change the line
checked={this.state.checkedItems.get(item.name)}
to this
checked={!!this.state.checkedItems.get(item.name)}
React gives you this warning because it likes that you chose between controlled and uncontrolled components.
In the case of a checkbox input the component is considered controlled when its checked prop is not undefined.
I've just given a default value for checked and changed the code testing for undefined a little bit.
The warning should be gone.
// import React, { Component } from "react";
class Checkbox extends React.Component {
static defaultProps = {
checked: false
}
render() {
return (
<input
type={this.props.type}
name={this.props.name}
checked={this.props.checked}
onChange={this.props.onChange}
/>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
checkedItems: new Map()
};
this.handleChange = this.handleChange.bind(this);
}
handleChange = e => {
const item = e.target.name;
const isChecked = e.target.checked;
this.setState(prevState => ({
checkedItems: prevState.checkedItems.set(item, isChecked)
}));
};
deleteCheckboxState = (name, checked) => {
const updateChecked = checked == null ? true : false;
this.setState(prevState => prevState.checkedItems.set(name, updateChecked));
};
clearAllCheckboxes = () => {
const clearCheckedItems = new Map();
this.setState({ checkedItems: clearCheckedItems });
};
render() {
const checkboxes = [
{
name: "check-box-1",
key: "checkBox1",
label: "Check Box 1"
},
{
name: "check-box-2",
key: "checkBox2",
label: "Check Box 2"
},
{
name: "check-box-3",
key: "checkBox3",
label: "Check Box 3"
},
{
name: "check-box-4",
key: "checkBox4",
label: "Check Box 4"
}
];
const checkboxesToRender = checkboxes.map(item => {
return (
<label key={item.key}>
{item.name}
<Checkbox
name={item.name}
checked={this.state.checkedItems.get(item.name) || false}
onChange={this.handleChange}
type="checkbox"
/>
</label>
);
});
const checkboxesDeleteHandlers = checkboxes.map(item => {
return (
<span
key={item.name}
onClick={() =>
this.deleteCheckboxState(
item.name,
this.state.checkedItems.get(item.name)
)
}
>
{item.name}
</span>
);
});
return (
<div className="App">
{checkboxesToRender}
<br /> {checkboxesDeleteHandlers}
<p onClick={this.clearAllCheckboxes}>clear all</p>
</div>
);
}
}
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>
I have a question about why does not the "onClick" function work? It will only receive "You are not old enough!", when i hit the button. I use a input field.
import React, { Component } from 'react';
class App extends Component {
constructor() {
super();
this.state= {
term: 'write a number'
}
this.change = this.change.bind(this);
}
change = (event) => {
this.setState({term: event.target.value >= 18 ? <p>You are old enough!
</p> : <p>You are not old enough!</p>});
}
render() {
return (
<div style={{textAlign : "center"}}>
<input type="text"></input><br></br>
<p>Result</p><br></br>
{this.state.term}
<button type="submit" onClick={this.change}>Submit</button>
</div>
);
}
}
export default App;
If you want to validate the input on click, store the value of the input in state.
class App extends Component {
constructor() {
super();
this.state = {
term: 'write a number',
value: ''
};
}
handleChange = event => {
this.setState({
value: event.target.value
});
};
validate = () => {
this.setState({
term:
parseInt(this.state.value) >= 18
? 'You are old enough!'
: 'You are not old enough!'
});
};
render() {
return (
<div style={{ textAlign: 'center' }}>
<input
type="text"
onChange={this.handleChange}
value={this.state.value}
/>
<br />
<p>Result</p>
<br />
<p>{this.state.term}</p>
<button type="submit" onClick={this.validate}>
Submit
</button>
</div>
);
}
}
You can create a handler for the input and when you click in the button you get the value from the state.
Check it out my approach.
class App extends React.Component {
state = {
age: null,
term: 'write a number'
}
onClick = () => {
if(this.state.age) {
const output = this.state.age >= 18 ?
<p>You are old enough!</p> :
<p>You are not old enough!</p>
this.setState({
term: output
});
}
onInputHandler = (event) => {
this.setState({age: event.target.value})
}
render() {
return (
<div style={{textAlign : "center"}}>
<input type="text" onChange={e => this.onInputHandler(e)}></input><br></br>
<p>Result</p><br></br>
<button onClick={this.onClick}>Submit</button>
</div>);
}
}
import React from 'react';
import './MenuCard.css';
class MenuCard extends React.Component {
constructor(props) {
super(props);
this.state = {
showButton: false,
hideButton: true,
aValue: 1,
breads: [],
category: [],
ids: 0
};
this.onShowButton = this.onShowButton.bind(this);
}
onShowButton = (id) => {
this.setState({
showButton: !this.state.showButton,
hideButton: !this.state.hideButton
}));
}
onValueIncrease = () => {
this.setState({aValue: this.state.aValue + 1});
}
onValueDecrease = () => {
this.setState({aValue: this.state.aValue - 1});
}
render() {
return (
<div>
{this.state.category.map(types => {
return (<div>
<div className="menu-head">{types}</div>
< div className="container-menu">
{this.state.breads.map((d, id)=> {
if (d.category === types) {
return (
<div className="content">
<div className="items"> {d.item_name}</div>
<div className="prices"> {d.price} Rs.</div>
<button id ={id} onClick={() => this.onShowButton(d.id)}
hidden={this.state.showButton}
className="add-menu-btn"> add
</button>
<span key={d.id} hidden={this.state.hideButton}>
<button id={d.id} className="grp-btn-minus"
onClick={this.state.aValue <= 1 ? () => this.onShowButton(d.id) : () => this.onValueDecrease(d.id)}>-
</button>
<input className="grp-btn-text" type="text"
value={this.state.aValue} readOnly/>
<button id={d.id} className="grp-btn-plus"
onClick={() => this.onValueIncrease(d.id)}>+
</button>
</span>
</div>
)
}
})}
</div>
</div>)
})}
</div>
)
}
There are multiple buttons according to items 1.
And here the problem when I click on single button all get updated I need only a single button to click with a single update 2
You need to keep the values in an array in the state, i.e:
values: [
{ id: 1, value: 20},
{ id: 2, value: 1}
]
If you then need to set the state, could look like this:
const values = Object.assign({}, this.state.values, { [id]: value })
this.setState({ values })
To get the value from state:
const value = this.state.values[id]
I'm learning ReactJS and I'm creating a editable Pokémon list based on this guide.
When I try to pass a function to edit a list item (at this point I want to click the item and get the name), I get TypeError: Cannot read property 'edit' of undefined on the following line of the addPokemon function: onClick={() => this.edit(pokemon.name)}
Code:
PkmnForm
import React, { Component } from "react";
import PkmnList from "./PkmnList";
class PkmnForm extends Component {
static types = [
'Bug',
'Dragon',
'Ice',
'Fighting',
'Fire',
'Flying',
'Grass',
'Ghost',
'Ground',
'Electric',
'Normal',
'Poison',
'Psychic',
'Rock',
'Water'
]
constructor(props) {
super(props);
this.state = {
name: '',
type: '',
pokemons: [],
caught: false,
};
}
handleSubmit = (event) => {
var pokemon = {
name: this.state.name,
type: this.state.type,
caught: this.state.caught
};
this.setState({
name: '',
type: '',
caught: false,
pokemons: this.state.pokemons.concat(pokemon)
});
event.preventDefault()
}
handleChange = (event) => {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleTypeChange = (event) => {
this.setState({
type: event.target.value,
})
}
editPokemon(name) {
console.log(name);
}
render() {
return (
<div>
<div>
<h1>Register a Pokémon</h1>
<form onSubmit={this.handleSubmit}>
<input
required
placeholder=" Name"
name="name"
onChange={this.handleChange}
value={this.state.name}
/>
<br />
<select
name="type"
required
value={this.state.type}
onChange={this.handleChange}
>
<option value='' disabled>Type</option>
{PkmnForm.types.map(
optionValue => (
<option
key={optionValue}
value={optionValue}
>
{optionValue}
</option>
)
)}
</select>
<br />
<label>
Caught
<input
name="caught"
type="checkbox"
checked={this.state.caught}
onChange={this.handleChange}
/>
</label>
<br />
<button type="submit">Add Pokemon</button>
</form>
</div>
<div>
<PkmnList
pokemons={this.state.pokemons}
edit={this.editPokemon}
/>
</div>
</div>
);
}
}
export default PkmnForm;
PkmnList
import React, { Component } from 'react';
class PkmnList extends Component {
constructor(props) {
super(props);
this.edit = this.edit.bind(this);
}
edit(name) {
this.props.edit(name);
}
addPokemon(pokemon) {
return <li
onClick={() => this.edit(pokemon.name)}
key={pokemon.name}
>
{pokemon.name} – {pokemon.type} {pokemon.caught ? '(caught)' : ''}
</li>
}
render() {
var pokemons = this.props.pokemons;
var listItems = pokemons.map(this.addPokemon);
return (
<ul>
{listItems}
</ul>
);
}
}
export default PkmnList;
Thanks :-)
The problem is in this line:
var listItems = pokemons.map(this.addPokemon);
Here, you are passing this.addPokemon as the function to map. But that function is not bound to this, so inside of it, this is not available.
You either have to bind it explicitly by calling .bind(this), just like you did with the edit function:
var listItems = pokemons.map(this.addPokemon.bind(this));
Or you can pass an arrow function that calls the method:
var listItems = pokemons.map(x => this.addPokemon(x));
You need to bind editPokemon like so:
constructor(props) {
super(props);
this.editPokemon = this.editPokemon.bind(this);
}
Or you can also use arrow functions, which have proper scoping:
editPokemon = (pokemon) => {
...
}