I'm new to React and I want to set a value for Radio button, however after I check one of them, then it's not checked after I set the state for a property of an object.
Here is my code:
import React, {Component} from 'react';
class TestComponent extends Component {
constructor(props) {
super(props);
this.completeChange = this.completeChange.bind(this);
this.state = {
hero: {}
};
}
getHero() {
}
completeChange(event) {
var hero = { ...this.state.hero
};
hero.Complete = event.target.value;
this.setState({
hero: hero
}, () => {
//when the callback is called, the radio button is not checked
console.log(this.state.hero.Complete);
});
}
render() {
return (
<div className="complete">
<label>Choose one: </label>
<input type="radio" id="Complete" value="true" onChange={this.completeChange}
value='true' checked={this.state.hero.Complete == true} />Yes
<input type="radio" id="Complete" value="false" onChange={this.completeChange}
value='false' checked={this.state.hero.Complete == false} />No
</div>
);
}
}
export default TestComponent;
What is wrong here? Can anyone here solve my problem?
Thank you in advanced.
You are mutating the state.
Try it this way:
completeChange(event) {
const {hero} = this.state;
const nextHero = {
...hero,
Complete: true
}
this.setState({ hero: nextHero }, () => {
//when the callback is called, the radio button is not checked
console.log(this.state.hero.Complete);
});
}
If you want to toggle the state then i suggest using the function version of setState, because state updates are async and when ever your next state relies on previous state you should use the function argument of setState to ensure you get the most recent state.
Something like this:
completeChange(event) {
this.setState(state => {
const { hero } = state;
const nextHero = {
...hero,
Complete: !hero.Complete
};
return {
hero: nextHero
};
});
}
And if you want to set the state based on the value of the button that was clicked you can check event.target.value:
completeChange(event) {
const { hero } = this.state;
const isComplete = event.target.value === 'true';
const nextHero = {
...hero,
Complete: isComplete
};
this.setState({hero: nextHero});
}
Here is a running example of your code:
class TestComponent extends React.Component {
constructor(props) {
super(props);
this.completeChange = this.completeChange.bind(this);
this.state = {
hero: {}
};
}
getHero() {}
completeChange(event) {
const { hero } = this.state;
const isComplete = event.target.value === 'true';
const nextHero = {
...hero,
Complete: isComplete
};
this.setState({hero: nextHero});
}
render() {
return (
<div className="complete">
<label>Choose one: </label>
<input
type="radio"
name="Complete"
value="true"
onChange={this.completeChange}
checked={this.state.hero.Complete == true}
/>Yes
<input
type="radio"
name="Complete"
value="false"
onChange={this.completeChange}
checked={this.state.hero.Complete == false}
/>No
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<TestComponent />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"/>
By the way
Never do == against a Boolean:
this.state.hero.Complete == true
Its better to use the === instead in this case:
this.state.hero.Complete === true
I wrote a post about that if you're interested in more details.
You could directly check your e.target.value=='true' and set into your state like this.
You can check here is working stackblitz demo.
Code Snippet
class TestComponent extends Component {
constructor(props) {
super(props);
this.completeChange = this.completeChange.bind(this);
this.state = {
hero: {}
};
}
getHero() {
}
completeChange(event) {
this.setState({
hero: event.target.value == 'true'
}, () => {
//when the callback is called, the radio button is not checked
console.log(this.state.hero);
});
}
render() {
return (
<div className="complete">
<label>Choose one: </label>
<input type="radio" onChange={this.completeChange}
value={true} checked={this.state.hero} />Yes
<input type="radio" value={false} onChange={this.completeChange} checked={!this.state.hero} />No
</div>
);
}
}
render(<TestComponent />, document.getElementById('root'));
Related
I have a form that is supposed to update the initial state, I've followed many tutorials and my code looks the same as them but for some reason it doesn't update the state.
import React, { Component } from 'react';
class Create extends Component{
constructor(props){
super(props);
this.state = {
title: "",
body: "",
error: ""
}
}
onTitleChange(e) {
const title = e.target.value;
this.setState({title})
}
onBodyChange(e) {
const body = e.target.value;
this.setState({body})
}
onSubmit(e) {
e.preventDefault();
if(!this.state.title || !this.state.body){
this.setState(() => ({ error: "Please fill in all gaps"}))
} else {
this.setState(() => ({ error: "" }))
// Send info to the main page
alert(this.state.title);
}
}
render(){
return(
<div>
{this.state.error && <p>{this.state.error}</p>}
<form onSubmit = {this.onSubmit}>
<label>Put a title for your note</label>
<input
placeholder="Title"
type="text"
value={this.state.title}
autoFocus
onChange= {this.onTitleChange}
/>
<label>Write your note</label>
<textarea
placeholder="Note"
value={this.state.body}
autoFocus
onChange = {this.onBodyChange}
/>
<input type="submit" value="Submit"/>
</form>
</div>
);
}
}
export default Create;
When I check the current state in the React developer tools it shows that the state remains the same and I don't know why because there are not errors in the log.
I'm working with webpack, babel and react.
////////////////////
EDITED
////////////////////
I edited my code following the suggestions you guys gave me but still it doesn't work. An alert is supposed to appear when submitted the form but that doesn't get fired either, so I believe that non of the functions are getting fired.
This is my edited code:
import React, { Component } from 'react';
class Create extends Component{
constructor(props){
super(props);
this.onTitleChange = this.onTitleChange.bind(this);
this.onBodyChange = this.onBodyChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
title: "",
body: "",
error: ""
}
}
onTitleChange(e) {
const title = e.target.value;
this.setState((prevState) => {
return {
...prevState,
title
};
});
}
onBodyChange(e) {
const body = e.target.value;
this.setState((prevState) => {
return {
...prevState,
body
};
});
}
onSubmit(e) {
e.preventDefault();
if(!this.state.title || !this.state.body){
this.setState((prevState) => {
return {
...prevState,
error: "Please fill in all gaps"
};
});
} else {
this.setState((prevState) => {
return {
...prevState,
error: ""
};
});
// Send info to the main page
alert(this.state.title);
}
}
render(){
return(
<div>
{this.state.error && <p>{this.state.error}</p>}
<form onSubmit = {this.onSubmit}>
<label>Put a title for your note</label>
<input
placeholder="Title"
type="text"
value={this.state.title}
autoFocus
onChange= {this.onTitleChange}
/>
<label>Write your note</label>
<textarea
placeholder="Note"
value={this.state.body}
autoFocus
onChange = {this.onBodyChange}
/>
<input type="submit" value="Submit"/>
</form>
</div>
);
}
}
export default Create;
You should try binding the event handlers in the constructor, because it seems like this within those event handling functions could be undefined. The React documentation outlines why the binding is necessary, and here's another useful page on handling forms in React.
constructor(props) {
super(props);
...
this.onTitleChange = this.onTitleChange.bind(this);
this.onBodyChange = this.onBodyChange.bind(this);
this.onSubmitChange = this.onSubmitChange.bind(this);
}
An alternative to binding in the constructor would also be to use arrow functions for your event handlers which implicitly bind this.
class Create extends Component {
...
onTitleChange = () => { ... }
onBodyChange = () => { ... }
onSubmitChange = () => { ... }
}
EDIT: Can't comment on your post since my reputation is too low, but it seems like there's a typo in the changes you just made.
this.onSubmitC = this.onSubmit.bind(this) should be changed to
this.onSubmit = this.onSubmit.bind(this)
React's setState() accepts an object, not a function. So change your onSubmit() to this:
if(!this.state.title || !this.state.body){
this.setState({ error: "Please fill in all gaps"})
} else {
this.setState({ error: "" })
// Send info to the main page
alert(this.state.title);
}
It better to use the previous state and only update the required (input value) values.
in your case you are replacing the existing state (whole object) just with the title and the same goes for body onBodyChange
onTitleChange () {
const title = e.target.value;
this.setState((prevState) => {
return {
...prevState,
title
};
});
};
<
I was developing a react component to get a value inside a input and automatically show it in a tag, using refs.
All works fine, but the value shown is the previous value.
I really don't now how to fix this. I using the onChange event in the input to change the state of what will be shown, it is clear that the present value is not taken, but rather the previous value
class Conversor extends Component {
constructor(props){
super(props)
this.state = {
value: null
}
this.output = this.output.bind(this)
}
output(){
console.log(this.state)
this.refs.output.innerHTML = this.state.value
}
render() {
return (
<div>
<h2>{this.state.inputValue}</h2>
<input ref="input" type="text" onChange={() => {this.setState({ value: this.refs.input.value }); this.output()}}/>
<label ref="output"></label>
</div>
);
}
}
If i put the value "Hello World" in the input, the value shown is "Hello Worl", when it's have to be the "Hello World"
You can use event to do this and no need of output() function.
class Conversor extends Component {
constructor(props){
super(props)
this.state = {
value: null
}
}
render() {
return (
<div>
<h2>{this.state.inputValue}</h2>
<input ref="input" type="text" onChange={(e) => {this.setState({ value: e.target.value });}}/>
<label ref="output">{this.state.value}</label>
</div>
);
}
}
The best way to achieve your goal is not using the refs. Here is how you do it
class Conversor extends Component {
constructor(props){
super(props)
this.state = {};
}
handleChange = (e) => {
const { id, value } = e.target;
this.setState({
[id]: value
})
}
render() {
const { name, anotherName } = this.state;
return (
<div>
<h2>{name}</h2>
<input id="name" name="name" type="text" onChange={this.handleChange}/>
<h2>{anotherName}</h2>
<input id="anotherName" name="anotherName" type="text" onChange={this.handleChange}/>
</div>
);
}
}
If you still want to use the refs then do the following,
class Conversor extends Component {
constructor(props){
super(props)
this.state = {
value: null
}
}
output = (e) =>{
this.setState({value: e.target.value }, () => {
this.refs.output.innerHTML = this.state.value
})
}
render() {
return (
<div>
<input ref="input" type="text" onChange={this.output}/>
<label ref="output"></label>
</div>
);
}
}
You don't need to bind your input handler function at all. Instead of doing that, just use an arrow function like _handleInputTextChange . Check this out:
import React, { Component } from 'react';
class InputTextHandler extends Component {
constructor(props){
super(props)
this.state = {
inputValue: ''
}
}
_handleInputTextChange = e => {
const inputValue = e.target.value;
this.setState({inputValue})
console.log(inputValue)
}
render() {
return (
<div>
<input
type="text"
onChange={this._handleInputTextChange}/>
</div>
);
}
}
export default InputTextHandler;
Two things: grab the event value in the onChange method, and pass the this.output method as the second argument to setState which fires after the state has been updated which is not a synchronous operation.
render() {
return (
<div>
<h2>{this.state.inputValue}</h2>
<input ref="input" type="text" onChange={event => {this.setState({ value:event.target.value }, this.output)}}/>
<label ref="output"></label>
</div>
);
}
Try it here!
I have a main react component called 'App' which contain user input data in its state. The state is updated with setState() every time user enter new data. Then the state is passed as props to another component called 'IncomeList' which render the data on screen. However the IncomeList component is not getting updated state after user input some data.
class App extends React.Component {
constructor(props) {
super(props);
this.addData = this.addData.bind(this);
this.state = {
expenses: [],
income: [],
}
}
addData(data) {
if (data.type == 'income') {
this.setState((prevState) => {
income: prevState.income.push(data);
}, console.log(this.state.income));
} else if (data.type == 'expense') {
this.setState((prevState) => {
expenses: prevState.expenses.push(data);
})
}
}
render() {
return (
<div>
<UserInput addData={this.addData} />
<IncomeList income={this.state.income} />
</div>
);
}
}
// UserInput component which contain a form
class UserInput extends React.Component {
constructor(props) {
super(props);
this.addDataLocal = this.addDataLocal.bind(this);
}
addDataLocal(e) {
e.preventDefault();
const data = {
type: e.target.elements.type.value,
description: e.target.elements.description.value,
amount: e.target.elements.amount.value
}
this.props.addData(data);
}
render() {
return (
<div>
<form onSubmit={this.addDataLocal}>
<select name="type" id="input-type" name="type">
<option value="income">Income</option>
<option value="expense">Expense</option>
</select>
<input type="text" placeholder="decription..." name="description"/>
<input type="number" placeholder="amount..." name="amount"/>
<input type="submit" value="Add"/>
</form>
</div>
)
}
}
class IncomeList extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{
this.props.income.map((item) => {
return (
<IncomeListItem key={item.amount} item={item}/>
)
})
}
</div>
)
}
}
You don't return anything from this.setState. You need return an object to be merged with your current state.
addData(data) {
const key = data.type === 'income' ? 'income' : 'expenses';
this.setState(prevState => ({
// with Computed Property Names we can make our
// code concise and avoid conditionals
[key]: [...prevState[key], data]
}), console.log(this.state[key]));
}
your addData should be like this
addData(data) {
if (data.type == 'income') {
let income=[...this.state.income];
income.push(data);
this.setState({
income:income
})
} else if (data.type == 'expense') {
let expenses=[...this.state.expenses];
expenses.push(data);
this.setState({
expenses:expenses
});
}
}
With #Asaf Aviv input I have created a working fiddle. Hope this will help.
JS Fiddle
My input checkbox always returns true, when i mark it passsa true correctly, but when i unmark tb i get true. I would like the checkbox to return true when checked and false when unchecked, this way I would apply my logic in the handlesubmit function.
handleChange = e => {
const { name, value } = e.target;
console.log(name, value);
switch (name) {
case 'period': this.json.schedule = 'period'; break;
}
<input
type="checkbox"
name="period"
defaultValue
onChange={this.handleChange}
/>
Checkout the value of e.target.checked.
In your case, change this line: const { name, value } = e.target to include checked.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
checked: true
}
}
handleChange = (e) => {
const { checked } = e.target
this.setState({
checked: checked
})
}
render() {
return (
<div>
<input type="checkbox"
onChange={e => this.handleChange(e)}
defaultChecked={this.state.checked}/>
{this.state.checked.toString()}
</div>
)
}
}
ReactDOM.render((<App/>), document.getElementById('testing'))
<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="testing"></div>
You first need to define what check is considered? If it is check it is true and when it is not checked it is false. Here is some code to get you started.
{ this.state.data.map(function(item, index) { return (
<input type="checkbox" checked={item.value} onChange={this.handleChange.bind(this, index)}/>
);
}.bind(this))
}
You aren't checking wether the box is checked or not, try:
handleChange = e => {
if (document.getElementByClassName("period").checked) {
// box is checked
} else {
// box is unchecked
}
}
You can use useState.
import React, { useState } from 'react';
const App = () => {
const [Check, setCheck] = useState(false);
return (
<div>
<h1>{Check ? 'Checked' : 'Not checked'}</h1>
<input type="checkbox" onChange={(e) => setCheck(e.target.checked) }/>
</div>
);
};
export default App;
How do I render an indeterminate checkbox via JSX?
Here's what I've tried:
function ICB({what}) {
return <input type="checkbox"
checked={what === "checked"}
indeterminate={what === "indeterminate"} />;
}
However, indeterminate is not an attribute on the HTMLElement, but a property. How do I set properties from React / JSX?
Solution:
As most of the answers below use findDOMNode or string refs, both of which are no longer considered good practice in React, I've written a more modern implementation:
function ICB() {
const [state, setState] = React.useState(0);
const indetSetter = React.useCallback(el => {
if (el && state === 2) {
el.indeterminate = true;
}
}, [state]);
const advance = () => setState(prev => (prev + 1) % 3);
return <input type="checkbox"
checked={state === 1}
ref={indetSetter}
onClick={advance} />;
}
ReactDOM.render(<ICB />, document.getElementById("out"));
<div id="out"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
You can also use the ref function directly:
ReactDOM.render(
<label>
<input
type="checkbox"
ref={input => {
if (input) {
input.indeterminate = true;
}
}}
/>
{' '}
Un test
</label>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
I would probably create a composite component that encapsulates the necessary hooks to set or unset the checkbox's indeterminate property. It looks like you're using ES2015 syntax, so I'll use some of those features here.
class IndeterminateCheckbox extends React.Component {
componentDidMount() {
if (this.props.indeterminate === true) {
this._setIndeterminate(true);
}
}
componentDidUpdate(previousProps) {
if (previousProps.indeterminate !== this.props.indeterminate) {
this._setIndeterminate(this.props.indeterminate);
}
}
_setIndeterminate(indeterminate) {
const node = React.findDOMNode(this);
node.indeterminate = indeterminate;
}
render() {
const { indeterminate, type, ...props } = this.props;
return <input type="checkbox" {...props} />;
}
}
// elsewhere
render() {
return <IndeterminateCheckbox
checked={this.props.state === "checked"}
indeterminate={this.props.state === "indeterminate"} />
}
Working example: https://jsbin.com/hudemu/edit?js,output
You can use the componentDidMount step (which is invoked after the initial rendering) to set that property:
componentDidMount() {
React.findDOMNode(this).indeterminate = this.props.state === "indeterminate";
}
If you want that property to be updated with subsequent renders, do the same thing in componentDidUpdate also.
I'd suggest creating a simple component (code ported from coffeescript so mind you, might have some simple typos):
const React = require('react');
module.exports = class IndeterminateCheckbox extends React.Component {
componentDidMount() {
this.refs.box.indeterminate = this.props.indeterminate;
}
componentDidUpdate(prevProps, prevState) {
if(prevProps.indeterminate !== this.props.indeterminate) {
this.refs.box.indeterminate = this.props.indeterminate;
}
}
render() {
return <input {...this.props} ref="box" type="checkbox"/>;
}
}
Now you have a simple component that behaves exactly like a checkbox, that supports the indeterminate prop. Note there's plenty of room for improvements here, namely setting propTypes and proper defaults for some props, and of course implementing componentShouldUpdate to only do something when needed.
An alternative would be to use a ref attribute with a callback to set the property on the DOM node. For example:
render: function() {
return (
<input
type="checkbox"
ref={function(input) {
if (input != null) {
React.findDOMNode(input).indeterminate = this.props.indeterminate;
}}
{...this.props} />
)
}
Dont use React.findDOMNode(this).
It is risky.
export class SelectAll extends Component {
constructor(props) {
super(props);
this.state = {
checked: false
};
this.myRef = React.createRef();
this.onChange = this.onChange.bind(this);
this.update = this.update.bind(this);
}
onChange(e) {
const checked = e.target.checked;
this.setState({
checked: checked
});
this.selectAllNode.indeterminate = false;
}
update(state: {
checked: Boolean,
indeterminate: Boolean
}) {
this.setState({
checked: state.checked
});
this.myRef.current.indeterminate = state.indeterminate;
}
render() {
return ( <
input type = "checkbox"
name = "selectAll"
checked = {
this.state.checked
}
onChange = {
this.onChange
}
ref = {
this.myRef
}
/>
);
}
}
React v15 implementation:
import React from 'react';
export default class Checkbox extends React.Component {
componentDidMount() {
this.el.indeterminate = this.props.indeterminate;
}
componentDidUpdate(prevProps, prevState) {
if(prevProps.indeterminate !== this.props.indeterminate) {
this.el.indeterminate = this.props.indeterminate;
}
}
render() {
const {indeterminate, ...attrs} = this.props;
return <input ref={el => {this.el = el}} type="checkbox" {...attrs}/>;
}
}
Taken from my tutorial which shows how this works with the recent React features. I hope this helps someone who stumbles upon this older question:
const App = () => {
const [checked, setChecked] = React.useState(CHECKBOX_STATES.Empty);
const handleChange = () => {
let updatedChecked;
if (checked === CHECKBOX_STATES.Checked) {
updatedChecked = CHECKBOX_STATES.Empty;
} else if (checked === CHECKBOX_STATES.Empty) {
updatedChecked = CHECKBOX_STATES.Indeterminate;
} else if (checked === CHECKBOX_STATES.Indeterminate) {
updatedChecked = CHECKBOX_STATES.Checked;
}
setChecked(updatedChecked);
};
return (
<div>
<Checkbox
label="Value"
value={checked}
onChange={handleChange}
/>
<p>Is checked? {checked}</p>
</div>
);
};
const Checkbox = ({ label, value, onChange }) => {
const checkboxRef = React.useRef();
React.useEffect(() => {
if (value === CHECKBOX_STATES.Checked) {
checkboxRef.current.checked = true;
checkboxRef.current.indeterminate = false;
} else if (value === CHECKBOX_STATES.Empty) {
checkboxRef.current.checked = false;
checkboxRef.current.indeterminate = false;
} else if (value === CHECKBOX_STATES.Indeterminate) {
checkboxRef.current.checked = false;
checkboxRef.current.indeterminate = true;
}
}, [value]);
return (
<label>
<input ref={checkboxRef} type="checkbox" onChange={onChange} />
{label}
</label>
);
};