How to setState using RadioButton in ReactJs - javascript

I was trying to setState using the radiobuttons in ReactJs but I dont know why this is not working, maybe I have something missing in my code
var handleRadio = (event)=>{
this.setState({views:event.target.value})
}
This is my Function "handleRadio"
<input
type="radio"
checked={this.state.views === "streets-v11"}
onClick={handleRadio}
value="streets-v11"
/> Street Map <br />
<input
type="radio"
checked={this.state.views === 'outdoors-v11'}
onClick={handleRadio}
value="outdoors-v11"
/> Topo Map <br />
<input
type="radio"
checked={this.state.views === 'satellite-v9'}
onClick={handleRadio}
value="satellite-v9"
/> Satellite Map
and that's my code for buttons..
I was trying to call the function on radio button click and then according to that I was trying to set the value of state but this code is not working to setState in my project What's Wrong in this code ?
my state name is views initialized as an empty string....

I think you should use OnChange instead the OnClick prop
change onClick={handleRadio} to OnChange={handleRadio}

Here is a complete example
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
views: [
{ txt: 'streets-v11', value: false },
{ txt: 'outdoors-v11', value: true },
{ txt: 'satellite-v9', value: true }
]
};
this.handleRadio = this.handleRadio.bind(this);
}
handleRadio(e) {
let views = this.state.views.slice(0);
views = views.map((v) => {
if (v.txt === e.target.name) v.value = !v.value;
return v;
});
this.setState({ ...this.state, views });
}
render() {
return (
<h1>
{this.state.views.map((v) => {
return (
<div key={v.txt}>
<input
name={v.txt}
type="radio"
checked={v.value}
onClick={this.handleRadio}
/>
{v.txt}
</div>
);
})}
</h1>
);
}
}
render(<App />, document.querySelector('#root'));

Related

React checkbox feature with only single selection

I want to figure out whether my code is wrong or a bug. I think there is no problem, but it does not work...
The code I used is:
https://codepen.io/cadenzah/pen/wvwYLgj?editors=0010
class ItemView extends React.Component {
constructor(props) {
super(props)
this.state = {
options: [{
id: 1,
name: "Item 1"
},
{
id: 2,
name: "Item 2"
}],
optionSelected: 2
}
}
toggleCheckbox(e) {
console.log(e.target.id)
if (this.state.optionSelected === e.target.id) {
this.setState({
optionSelected: undefined
})
} else {
this.setState({ optionSelected: e.target.id })
}
}
render() {
return (
<div className="container">
<div className="row">
<ItemList
options={this.state.options}
optionSelected={this.state.optionSelected}
toggleCheckbox={(e) => this.toggleCheckbox(e)} />
</div>
</div>
)
}
}
const ItemList = ({ options, optionSelected, toggleCheckbox }) => {
return (
<div className="col s12">
{
options.map((option, index) => (
<Item
key={index}
option={option}
checked={(optionSelected === (index + 1) ? true : false)}
toggleCheckbox={toggleCheckbox} />
))
}
</div>
)
}
const Item = ({ option, checked, toggleCheckbox }) => {
return (
<div className="card">
<div className="card-content">
<p><label htmlFor={option.id}>
<input
className="filled-in"
type="checkbox"
id={option.id}
onChange={toggleCheckbox}
checked={(checked ? "checked" : "")} />
<span>{option.id}. {option.name}</span>
</label></p>
</div>
</div>
)
}
Code explaination:
React code, with materialize-css used.
It is a simple checkbox feature with multiple items, restricted to select only one item. So, if I check one of them, every item except for what I just selected will be unchecked automatically. If I uncheck what I just checked, every item will stay unchecked.
The core logic is: in <ItemList /> component, there is a conditional props that determines whether each item has to be checked or not. It compares the id, and hand in true or false into its children. That checked props is used in <Item /> component to set the checked attribute of <input>.
Strange thing is, as I set default choice in the initial state, when I just run the application, the check feature works as I expected. But if I click one of them, it does not work.
What is the problem of it?
You can check if the selected option is the checked one like this:
checked={optionSelected === option.id}
And then you simply get it into your input like this:
<input checked={checked} />
Also, make sure to change your state ids into strings (the DOM element id is of type string):
options: [{
id: '1',
name: "Item 1"
},
{
id: '2',
name: "Item 2"
}],
optionSelected: '2'
https://codepen.io/AndrewRed/pen/gOYBVPZ?editors=0010
class ItemView extends React.Component {
constructor(props) {
super(props)
this.state = {
options: [{
id: 1,
name: "Item 1"
},
{
id: 2,
name: "Item 2"
}],
optionSelected: 2
}
}
toggleCheckbox(e) {
this.setState({
optionSelected : e.target.id
})
}
render() {
return (
<div className="container">
<div className="row">
<ItemList
options={this.state.options}
optionSelected={this.state.optionSelected}
toggleCheckbox={(e) => this.toggleCheckbox(e)} />
</div>
</div>
)
}
}
const ItemList = ({ options, optionSelected, toggleCheckbox }) => {
return (
<div className="col s12">
{
options.map((option, index) => (
<Item
key={index}
option={option}
checked={(optionSelected === (index + 1) ? true : false)}
toggleCheckbox={toggleCheckbox}
optionSelected = {optionSelected}
/>
))
}
</div>
)
}
const Item = ({ option, checked, toggleCheckbox,optionSelected }) => {
return (
<div className="card">
<div className="card-content">
<p><label htmlFor={option.id}>
<input
className="filled-in"
type="checkbox"
id={option.id}
onChange={toggleCheckbox}
checked={option.id == optionSelected ? "checked" : ""} />
<span>{option.id}. {option.name}</span>
</label></p>
</div>
</div>
)
}
function tick() {
ReactDOM.render(
<ItemView />,
document.getElementById('root')
);
}
tick()
COPY PASTE AND RUN
e.target.id is a string while index is a number. When you do a === comparison the type is also checked and these are not the same. This results in checked always being false after the initial state (which you set yourself as an int)

How to fill dropdown with JSON data in React?

I try to fill in a dropdown with data from the JSON format but for now the dropdown is empty (no results found...)
I certainly have a mistake and I can not understand where I'm confusing.
I will attach a screen of my API.
I want to get Station and NameStation..
API for Stations
My code:
import React, { Component } from 'react';
import Select from 'react-select';
import 'react-select/dist/react-select.css';
function parseStations(stations){
return stations.map((station) => {
return { label: station.NameStation, value: station.Station };
});
}
export default class Weather extends Component {
constructor(props) {
super(props);
this.state = {
options: [
{ value: true, label: 'Yes' },
{ value: false, label: 'No' }
], stations: [
],
value: null
}
this.onChange = this.onChange.bind(this);
}
onChange(event) {
this.setState({ value: event.value });
console.log('Boolean Select value changed to', event.value);
}
componentDidMount() {
this.getStations();
}
getStations() {
fetch('http://localhost:56348/api/stations', {
data: 'Station',
data: 'NameStation',
method: "GET"
}).then(res => res.json())
.then(res => this.setState({ stations: parseStations(res.stations) }))
//.then(res => this.setState({ stations: res.stations }))
//.catch(e => )
}
render() {
return (
<div className="MasterSection">
<div className="wrapper">
<div className="section">Изберете № на станция</div>
<Select
onChange={this.onChange}
//options={this.state.options}
options={this.state.stations}
value={this.state.value}
clearable={false}
/>
</div>
<div class="section">
<input type="text" class="form-control" placeholder="Брой дни назад" aria-label="Username" aria-describedby="basic-addon1"></input>
</div>
<div class="section">
<button type="button" class="btn btn-outline-dark">Покажи</button>
</div>
</div>
);
}
}
Seems you made a typo naming the prop stations instead of options :
<Select
onChange={this.onChange}
options={this.state.stations} // here
value={this.state.value}
clearable={false}
/>
Edit : you'll need to parse your json first to pass a proper array of objects like this : [{ label: nameStation, value: Station }]
Edit 2 : Here's a parser for your data :
function parseStations(stations){
return stations.map((station) => {
return { label: station.NameStation, value: station.Station };
});
}
You can call this in your async request before setting the state :
.then(res => this.setState({ stations: parseStations(res.stations) }))
componentDidMount() is executed only after render() is completed. so there's no way getStations() gets executed at the time your UI gets rendered. it is not a good idea to setState inside componentDidMount() as it triggers re rendering. use componentWillMount() instead.
correct the typo that Dyo mentioned and use options={this.state.stations}

In React, how to bind an input's value when rendering a list of inputs?

I'm rendering a list of inputs and I want to bind each input's value to a link's href. My current attempt renders https://twitter.com/intent/tweet?text=undefined:
class App extends React.Component {
tweets = [
{ id: 1, link: 'example.com' },
{ id: 2, link: 'example2.com' }
];
render() {
return (
<div>
{this.tweets.map(tweet =>
<div key={tweet.id}>
<input type="text" placeholder="text" onChange={e => tweet.text = e.target.value} />
<a href={`https://twitter.com/intent/tweet?text=${tweet.text}`}>Tweet</a>
</div>
)}
</div>
);
}
}
This probably needs to involve setState but I have no idea how to achieve that when rendering a list. I've tried to do some research on this but didn't found anything helpful.
JSFiddle: https://jsfiddle.net/nunoarruda/u5c21wj9/3/
Any ideas?
You can move the tweets variable to the state to maintain consistency in that array.
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
tweets: [
{ id: 1, link: 'example.com' },
{ id: 2, link: 'example2.com' }
]
};
};
setTweets = index => e => {
const { tweets } = this.state
tweets[index].text = e.target.value
this.setState({ tweets })
}
render() {
const { tweets } = this.state
return (
<div>
{tweets.map((tweet, index) =>
<div key={tweet.id}>
<input type="text" placeholder="text" onChange={this.setTweets(index)} />
<a href={`https://twitter.com/intent/tweet?text=${tweet.text}`}>Tweet</a>
</div>
)}
</div>
);
}
}
Updated Jsfiddle: https://jsfiddle.net/u5c21wj9/6/
You can reach the desired result using state.
return (
<div>
{tweets.map(({ id, link }) =>
<div key={id}>
<input type="text" placeholder="text" onChange={({ target }) => this.setState({ [id]: target.value })} />
<a href={`https://twitter.com/intent/tweet?text=${this.state[id] || link}`}>Tweet</a>
</div>
)}
</div>
);
Note: I would move tweets outside the component and implement few ES6 features.
Updated Jsfiddle: https://jsfiddle.net/u5c21wj9/7/
You really should use a state here and make your tweets variable be part of it. To do that, add a constructor:
constructor() {
super();
this.state = {
tweets: [
{ id: 1, link: 'example.com' },
{ id: 2, link: 'example2.com' }
]
};
}
Then you need to mutate each linkwhenever you type in one of the inputs. There are a few pitfalls here, so let me go through them one-by-one:
changeTweet = (id, e) => {
let arr = this.state.tweets.slice();
let index = arr.findIndex(i => i.id === id);
let obj = Object.assign({}, arr[index]);
obj.link = e.target.value;
arr[index] = obj;
this.setState({tweets: arr});
}
First, you need to create a copy of your state variable. This gives you something to work with, without mutating the state directly which is anti-pattern. This can be done with slice().
Since you are sending in the id of the object to modify, we need to find it in our array (in case the items are unordered). This is done with findIndex(). You might want to handle the scenario in which such index is not found (I have not done that).
Now we know where in the array the object with the given id key is. Now, create a copy of that item (which is an object). This is also to prevent mutating the state directly. Do this with Object.assign().
Now change the link to the input value we typed in. Replace the old item object with the new one (obj) and replace the old tweets array with the new one (arr).
Here's the full example:
class App extends React.Component {
constructor() {
super();
this.state = {
tweets: [
{ id: 1, link: 'example.com' },
{ id: 2, link: 'example2.com' }
]
};
}
changeTweet = (id, e) => {
let arr = this.state.tweets.slice();
let index = arr.findIndex(i => i.id === id);
let obj = Object.assign({}, arr[index]);
obj.link = e.target.value;
arr[index] = obj;
this.setState({tweets: arr});
}
render() {
return (
<div>
{this.state.tweets.map(tweet =>
<div key={tweet.id}>
<input type="text" placeholder="text" onChange={(e) => this.changeTweet(tweet.id, e)} />
<a href={`https://twitter.com/intent/tweet?text=${tweet.link}`}>Tweet</a>
</div>
)}
</div>
);
}
}
ReactDOM.render(<App />, 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>
You need to save the text from the input in the state (using setState), not in the tweets array. Then you can render it getting the text from the state.
class App extends React.Component {
tweets = [
{ id: 1, link: 'example.com' },
{ id: 2, link: 'example2.com' }
];
state = {
tweetsText :{}
}
handleTextChange = (event, tweetId) => {
const tweetsTextCopy = Object.assign({}, this.state.tweetsText)
tweetsTextCopy[tweetId] = event.target.value
this.setState({tweetsText: tweetsTextCopy})
}
render() {
return (
<div>
{this.tweets.map(tweet =>
<div key={tweet.id}>
<input type="text" placeholder="text" onChange={e => this.handleTextChange(e, tweet.id)} />
<a href={`https://twitter.com/intent/tweet?text=${this.state.tweetsText[tweet.id]}`}>Tweet</a>
</div>
)}
</div>
);
}
}
Links info is in the link property of your tweets array. The property text is not defined.
So, your render function should look like this
render() {
return (
<div>
{this.tweets.map(tweet =>
<div key={tweet.id}>
<input type="text" placeholder="text" onChange={e => tweet.text= e.target.value} />
<a href={`https://twitter.com/intent/tweet?text=${tweet.link}`}>Tweet</a>
</div>
)}
</div>
);
}

React: Adding an input component via a button and updating its state

I am trying to create a button that will add a new input element to a page and then as I type display its changes.
However when I type into the input fields in <Input />, for some reason the state isn't changing. The input fields stay blank.
Out of curiosity, I removed the button that adds the <Input /> component and ran it with one <Input /> field on the page. When I type into one of the input fields, I can see my text.
It seems that when I add a new component to the page and try to change the state, something is off.
What am I doing wrong?
function Input(props) {
console.log(props)
return (
<div>
<div><input name="pitchName" value={props.currentValue.pitchName} placeholder="Pitch Name" onChange = {props.updateNewPitch}/></div>
<div><input name="shortCut" value={props.currentValue.shortcut} placeholder="Short cut" onChange = {props.updateNewPitch} /></div>
<div><input name="subject" value={props.currentValue.subject} placeholder="Subject" onChange = {props.updateNewPitch} /></div>
<div><textarea name="pitch" value={props.currentValue.pitch} onChange = {props.updateNewPitch}/></div>
<button type="submit" onClick={props.savePitch} >Add Pitch</button>
</div>
)
}
// function SavedPitches(props)
class Form extends React.Component{
constructor(props){
super(props);
this.state = {
inputList: [],
addNewPitch: {
pitchName: '',
shortCut: '',
subject: '',
pitch: ''
},
savedPitches: []
};
this.onAddBtnClick = this.onAddBtnClick.bind(this)
this.savePitch = this.savePitch.bind(this)
this.updateNewPitch = this.updateNewPitch.bind(this)
}
updateNewPitch(e){
this.setState({addNewPitch: {...this.state.addNewPitch, [e.target.name]: e.target.value}})
}
onAddBtnClick(event){
const inputList = this.state.inputList;
this.setState({
inputList: inputList.concat(
<Input savePitch={this.savePitch}
currentValue = {this.state.addNewPitch}
updateNewPitch={this.updateNewPitch}
/>
)
})
}
render() {
return(
<div>
<button onClick={this.onAddBtnClick}>Add input</button>
<div></div>
{
this.state.inputList
}
</div>
)
}
}
ReactDOM.render(<Form />,document.getElementById('root'));
Reason is because you are storing the Input (UI element) in state variable, and that variable is not getting update only values are getting updated in a separate state variable addNewPitch.
Suggestion:
1- Storing UI elements in state variable is not a good idea, always store value in state and all the ui logic should be inside render function.
2- Use a state variable and toggle the Input (UI element) on the basis of that.
Check working solution (check the values on addNewPitch inside render it will get updated properly):
function Input(props) {
return (
<div>
<div><input name="pitchName" value={props.currentValue.pitchName} placeholder="Pitch Name" onChange = {props.updateNewPitch}/></div>
<div><input name="shortCut" value={props.currentValue.shortcut} placeholder="Short cut" onChange = {props.updateNewPitch} /></div>
<div><input name="subject" value={props.currentValue.subject} placeholder="Subject" onChange = {props.updateNewPitch} /></div>
<div><textarea name="pitch" value={props.currentValue.pitch} onChange = {props.updateNewPitch}/></div>
<button type="submit" onClick={props.savePitch} >Add Pitch</button>
</div>
)
}
class Form extends React.Component{
constructor(props){
super(props);
this.state = {
inputList: [],
addNewPitch: {
pitchName: '',
shortCut: '',
subject: '',
pitch: ''
},
savedPitches: []
};
this.onAddBtnClick = this.onAddBtnClick.bind(this)
this.savePitch = this.savePitch.bind(this)
this.updateNewPitch = this.updateNewPitch.bind(this)
}
savePitch() {
}
updateNewPitch(e){
this.setState({addNewPitch: {...this.state.addNewPitch, [e.target.name]: e.target.value}})
}
onAddBtnClick(event){
const inputList = this.state.inputList;
this.setState({
show: true,
addNewPitch: {
pitchName: '',
shortCut: '',
subject: '',
pitch: ''
}
})
}
render() {
console.log('addNewPitch', this.state.addNewPitch);
return(
<div>
<button onClick={this.onAddBtnClick}>Add input</button>
<div></div>
{
this.state.show && <Input
savePitch={this.savePitch}
currentValue = {this.state.addNewPitch}
updateNewPitch={this.updateNewPitch}
/>
}
</div>
)
}
}
ReactDOM.render(<Form />,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'/>

Count checked checkboxes in React.js

I'm using a document structure like this
render() {
return (
<div className="MyComponent">
<ul className="">
{parseRecommendations(this.props.recommendations)}
</ul>
</div>
);
}
function parseRecomendations(recommendations) {
return recommendations.map((recommendation, key) => {
return (<Recommendation data={recommendation} key={key} />);
});
}
Where each Recommendation is its own component containing a checkbox
class Recommendation extends Component {
const recommendation = this.props.data;
const pageUrl = recommendation.url;
return (
<li className="article-item" key={key}>
<div className="article-info">
<input type="checkbox" defaultChecked="checked" aria-described-by={recommendation.pii} />
<a className="journal-title" href={pageUrl} id={recommendation.pii}>{recommendation.title}</a>
</div>
</li>
);
I'd like to have a title saying [Download (x) PDFs], where x is the number of selected checkboxes. How do I find the value of x in this case?
You need to store information about whether input is "checked" in your data. Then, simply count items with truthy "checked" flag.
Here is my solution. You should be able to get principle here and modify your code.
const data = [
{ checked: false, value: 'document 1' },
{ checked: true, value: 'document 2' },
{ checked: true, value: 'document 3' },
{ checked: false, value: 'document 4' },
{ checked: false, value: 'document 5' },
];
const Item = props => (
<div>
<input type="checkbox" checked={props.checked} onChange={props.onCheckChange} />
{ props.value }
</div>
)
var Hello = React.createClass({
getInitialState() {
return {
items: this.props.items.concat(),
};
},
onCheckChange(idx) {
return () => {
const items = this.state.items.concat();
items[idx].checked = !items[idx].checked;
this.setState({items});
}
},
totalChecked() {
return this.state.items.filter(props => props.checked).length;
},
render() {
return (
<div>
{ this.state.items.map((props, idx) => (
<Item {...props} key={idx} onCheckChange={this.onCheckChange(idx)} />
)) }
Total checked: { this.totalChecked() }
</div>
);
}
});
ReactDOM.render(
<Hello items={data} />,
document.getElementById('container')
);
If you just want to get the number of selected check-boxes you can try this
let checkedBoxes = document.querySelectorAll('input[name=chkBox]:checked');
Then get the total checked boxes via checkedBoxes.length
Edit:
Instead of querying whole document. You can get the nearest possible parent via getElementsByClassName or getElementById and then apply querySelectorAll on that element.
e.g
let elem = document.getElementsByClassName("MyComponent");
let checkedBoxes = elem.querySelectorAll('input[name=chkBox]:checked');
You also could obtain the total of selected checkboxes by element type. The "console.log(totalSelectedCheckboxes)" will print them when the state of totalSelectedCheckboxes change using useEffect Hook.
import React, { useState, useEffect } from 'react';
const RenderCheckboxes = () => {
const [totalSelectedCheckboxes, setTotalSelectedCheckboxes] = useState(0);
function handleChk() {
setTotalSelectedCheckboxes(document.querySelectorAll('input[type=checkbox]:checked').length);
}
useEffect(() => {
console.log(totalSelectedCheckboxes);
}, [totalSelectedCheckboxes]);
return (<div>
<div>
<input type="checkbox" value={1} onChange={() => handleChk()} />Chk1
</div>
<div>
<input type="checkbox" value={2} onChange={() => handleChk()} />Chk2
</div>
<div>
<input type="checkbox" value={2} onChange={() => handleChk()} />Chk2
</div>
</div>);
}
export default RenderCheckboxes;

Categories