Setting values from radio buttons generated with map in react js - javascript

I am trying to :
generate radio buttons from a constant array using Map in react
let user select one and set the state with handleChange()
With the following code I was able to achieve 1, but for some reason when I try to display with handleChange() I see it is an empty string.
Could you please help me ?
Thanks
import React, { Component } from "react";
const members = ["Araki", "Ibata", "Fukutome", "Woods", "Alex", "Tatsunami"];
export default class MyRadio extends Component {
constructor(props) {
super(props);
this.state = {
lastName: "",
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
console.log("handleChange() e:" + e.target.value);
this.setState({
[e.target.name]: e.target.value,
});
}
render() {
console.log("render");
return (
<form>
<div>
{members.map((item) => (
<React.Fragment>
<label htmlFor={item.name}> {item}</label>
<input
name="lastName"
key={item.name}
id={item.name}
value={item.name}
type="radio"
onChange={this.handleChange}
/>
</React.Fragment>
))}
</div>
<div></div>
</form>
);
}
}

To make this workable solution you have to change the members as follow
const members = [{name: "Araki", name: "Ibata", ...}];
array should be a object array with each object has name property because in the map you are expecting name should be there as item.name.
Or either you have to change the loop without item.name you have to use item
{members.map((item) => (
<React.Fragment>
<label htmlFor={item}> {item}</label>
<input
name="lastName"
key={item}
id={item}
value={item}
type="radio"
onChange={this.handleChange}
/>
</React.Fragment>
))}

Related

ReactJs Making changes from dynamically added input elements to corresponding html elements

//App.js
class App extends Component {
constructor(props) {
super(props);
this.state = {
formVal: {},
showMaker: true,
showTemplate: false,
uniMakerArr: [],
uniTemplateArr: [],
this.handleChange = this.handleChange.bind(this);
this.onClickMaker = this.onClickMaker.bind(this);
this.onClickTemplate = this.onClickTemplate.bind(this);
this.addUniFn = this.addUniFn.bind(this);
handleChange = (e) => {
let formVal = this.state.formVal;
let name = e.target.name;
let val = e.target.value;
formVal[name] = val;
this.setState({formVal})
};
addUniFn(e) {
const {uniMakerArr, uniTemplateArr} = this.state;
uniMakerArr.push(<UniMaker key={uniqid()} />);
uniTemplateArr.push(<UniTemplate key={uniqid()} />)
this.setState({
uniMakerArr, uniTemplateArr
});
};
onClickMaker(e) {
this.setState({
showMaker: true,
showTemplate: false,
});
};
onClickTemplate(e) {
this.setState({
showMaker: false,
showTemplate: true,
});
};
render() {
const {firstName, lastName} = this.state.formVal;
const {showMaker, showTemplate, uniMakerArr, uniTemplateArr} = this.state;
return (
<div>
<header className="header">
Curriculum Vitae!
</header>
<nav className="nav">
<a onClick={this.onClickMaker}>Maker</a>
<div className='vl'></div>
<a onClick={this.onClickTemplate}>Preview</a>
</nav>
<div>
{showMaker ? <Maker
handleChange={this.handleChange} add={this.addUniFn}
uniMakerState={uniMakerArr} /> : null}
{showTemplate ? <Preview
uniMakerArr={uniMakerArr}
uniTemplateArr={uniTemplateArr}
//state value for object "formVal"
firstName={firstName}
lastName={lastName}/> : null}
</div>
</div>
);
}
}
export default App;
Here is component Maker:
class Maker extends Component {
constructor(props) {
super(props);
}
render() {
const {handleChange, formVal, add, uniMakerState} = this.props;
return(
<div className='maker'>
<p className="headerText">Personal Information</p>
<form className='personalInp'>
<label htmlFor='firstName'></label>
<input type="text" name="firstName" value={formVal} onChange={handleChange} placeholder="First Name"></input>
<label htmlFor='lastName'></label>
<input type="text" name="lastName" value={formVal} onChange={handleChange} placeholder="Last Name"></input>
{uniMakerState.map((component) => {
return <UniMaker formVal={formVal} handleChange={handleChange}
key={component.key} }/>
})}
<button type="button" className="addBtn" onClick={add}>Add</button>
</form>
<div/>
)
}
}
export default Maker;
Here is component Preview:
class Preview extends Component {
constructor(props) {
super(props);
}
render() {
const {firstName, lastName,uniTemplateArr} = this.props;
return (
<div className='template'>
<p className="name">{firstName || 'Your'} {lastName || 'Name'}</p>
{uniTemplateArr.map(component => <UniTemplate />)}
</div>
)
}
}
export default Preview;
So what this basically does is add two-component when clicking the add button to Maker and Preview tabs. These two components are child components to Maker and Preview components. And they are identical to these two components. This means They will add two more HTML input elements and the fields where the values can be shown.
But the problem is when I make changes in the form that is in the Maker tab. It only reads and changes the value for the first two fields that are in the Preview tab. But ignores the other dynamically added fields. How can I make changes to my code so that the dynamically added input fields relate to the other component that was also added dynamically and only changes values to the corresponding input elements to the divs field?
I've tried putting unique keys to the input elements. But I don't know how to use those keys to update the values for the correct fields. Lastly, I'm sorry if the code is not so readable and the question is confusing. This is my first question, and I tried to be as thorough and transparent as possible. Thanks!

How to keep cursor position in a react input element

The cursor keeps going to the end. How to keep the cursor position when editing from the the middle of the string?
Code that I am using is:
const rootElement = document.getElementById('root');
class MyFancyForm extends React.Component {
constructor(props) {
super(props);
this.state = {myValue: ""};
}
handleCommaSeparatedChange = event => {
const {value} = event.target;
this.setState({myValue: value});
};
render() {
return(
<form >
<div>
<label>
Cursor position looser
<br />
<input onChange={this.handleCommaSeparatedChange} value={this.state.myValue} />
</label>
</div>
</form>
)
}
}
const element = <MyFancyForm />;
ReactDOM.render(element, rootElement);
Any idea how could I achieve it?
just change value into defaultValue - it worked both in codepen and codesandbox for me
class MyFancyForm extends React.Component {
constructor(props) {
super(props);
this.state = {myValue: ""};
}
handleCommaSeparatedChange = event => {
const {value} = event.target;
this.setState({myValue: value});
};
render() {
return(
<form >
<div>
<label>
Cursor position looser
<br />
<input onChange={this.handleCommaSeparatedChange} defaultValue={this.state.myValue} />
</label>
</div>
</form>
)
}
}
ReactDOM.render(
<MyFancyForm />,
document.getElementById('root')
);
I know it's an old post but this might help someone.
I found this snippet in one github issue and helped me to get around this problem.
onChange={(event) => {
event.persist()
const caretStart = event.target.selectionStart;
const caretEnd = event.target.selectionEnd;
// update the state and reset the caret
this.updateState();
event.target.setSelectionRange(caretStart, caretEnd);
}}
Quote from:
https://github.com/facebook/react/issues/955#issuecomment-469344232
I solved this by creating a TextInput component that wraps <input type="text"> and proxying the value in internal state.
function TextInput({ value, onChange }) {
// Create a proxy value in internal state to prevent the caret from jumping to the end every time the value updates
const [currentValue, setCurrentValue] = useState<string>(value);
useEffect(() => {
setCurrentValue(value);
}, [value]);
return (<input
type="text"
value={currentValue}
onChange={(e) => {
setCurrentValue(e.target.value);
onChange(e.target.value);
}}
/>);
}
Then I use it in a parent component like so. It works well so far.
<TextInput
value={textValue}
onChange={(e) => {
setTextValue(e);
}}
/>

Printing list in <ul>

I want to print a new <ul> list of <li> movies.
I don't see any list nor elements.
I also get a warning:
index.js:2178 Warning: A component is changing an uncontrolled input of type text 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. More info: https://reactjs.org/docs/forms.html#controlled-components
in input (at index.js:54)
in label (at index.js:52)
in form (at index.js:51)
in div (at index.js:50)
in Movie (at index.js:70)
This is my code:
class Movie extends React.Component {
constructor(props) {
super(props);
this.state = {value: '',
list: [],
checked: true
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.addMovie = this.addMovie.bind(this);
this.listMovies = this.listMovies.bind(this);
}
handleChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleSubmit(event) {
event.preventDefault();
this.addMovie();
}
addMovie(value){
this.setState({ list: [...this.state.list, value] });
console.log(...this.state.list);
}
listMovies(){
return(
<ul>
{this.state.list.map((item) => <li key={this.state.value}>{this.state.value}</li>)}
</ul>
);
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
Movie name:
<input name="movieName" type="text" value={this.state.movieName} onChange={this.handleChange} />
Favorite?
<input name="favorite" type="checkbox" checked={this.state.favorite} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
<button onClick={this.listMovies}>
List Movies
</button>
</div>
);
}
}
ReactDOM.render(
<Movie />,
document.getElementById('root')
);
I would really want to print only my Favorites movies
I'm guessing you want a simple movies list with favorites. Not the best one but working code:
import React from 'react';
import { render } from 'react-dom';
class App extends React.Component {
state = {
favorite: false,
movieName: "",
movies: [],
filter: true,
};
handleChange = (event) =>
event.target.name === "favorite"
? this.setState({ [event.target.name]: event.target.checked })
: this.setState( { [ event.target.name]: event.target.value } );
handleSubmit = ( event ) => {
event.preventDefault();
this.setState({
movies: [...this.state.movies, {name: this.state.movieName, favorite: this.state.favorite }]
});
}
listFavoriteMovies = () => (
<ul>
{this.state.movies
.filter( movie => movie.favorite )
.map( movie => <li>{movie.name}</li>)}
</ul>
);
listAllMovies = () => (
<ul>
{this.state.movies
.map(movie => <li>{movie.name}</li>)}
</ul>
);
changeFilter = () =>
this.setState( prevState => ( {
filter: !prevState.filter,
}))
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
Movie name:
<input name="movieName" type="text" onChange={this.handleChange} />
Favorite?
<input name="favorite" type="checkbox" onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
<p>Showing only favorite movies.</p>
<ul>
{
this.state.filter
? this.listFavoriteMovies()
: this.listAllMovies()
}
</ul>
<button onClick={this.changeFilter}>Click toggle for all/favorites.</button>
</div>
);
}
}
render(<App />, document.getElementById('root'));
if you initially pass undefined or null as the value prop, the
component starts life as an "uncontrolled" component. Once you
interact with the component, we set a value and react changes it to a
"controlled" component, and issues the warning.
In your code initialise movieName in your state to get rid of warning.
For more information check here

Creating a form in React that saves data

I am trying to create a customer details form in react (currently using react-json-form) where I can reuse the values in the inputs to create a saved file that the app can refer to. I have created the form and can output the results but I am unsure how to save the input values for future use or call them back once they are saved.
If anyone has any suggestions or examples of a form that does this then I would be greatly appreciative.
My code is as follows:
import React, { Component } from 'react';
import JSONTree from 'react-json-tree';
import { BasicForm as Form, Nest, createInput } from 'react-json-form';
const Input = createInput()(props => <input type="text" {...props} />);
const UserFields = () => (
<section>
<h3>User</h3>
<div>Name: <Input path="name" /></div>
<div>Email: <Input path="email" /></div>
</section>
);
export default class ExampleForm extends Component {
state = { data: {} };
updateData = data => this.setState({ data });
render() {
return (
<Form onSubmit={this.updateData}>
<Nest path="user">
<UserFields />
</Nest>
<button type="submit">Submit</button>
<JSONTree data={this.state.data} shouldExpandNode={() => true} />
</Form>
);
}
}
A more simple solution would be to use a form, like a semanti-ui-react form, store the information to the state onChange, then convert the info to JSON for storage.
import { Form, Button } from 'semantic-ui-react'
export default class App extends Component {
constructor() {
super()
this.state = {
name: "",
email: ""
}
}
handleChange = (e, {name, value}) => {
console.log(name, value)
this.setState({[name]: value})
}
render() {
return (
<div>
<Form onSubmit={this.sendDataSomewhere}>
<Form.Field>
<Form.Input name="name" value={this.state.name} onChange={this.handleChange}/>
</Form.Field>
<Form.Field>
<Form.Input name="email" value={this.state.email} onChange={this.handleChange}/>
</Form.Field>
<Button type="submit">Submit</Button>
</Form>
</div>
)
}
}
I use a dynamic method of receiving the input from different fields using the name and val attributes. The values captured in state are then accessible by this.state.whatever
Hope this helped

React - Forms - How to deal with child component updates

I have a form component that has a state containing an array of items.
I am having a hard time trying to update the state of the form when one of the item inputs gets updated.
At first I was creating a state on the items themselves and updating the values using the following code:
class ItemRow extends Component{
constructor(props){
super(props)
this.state = this.props.item;
}
updateItem(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
.....
render(){
return (
<FormControl
type="text"
name="name"
value={this.state.name}
onChange={this.updateItem}
/>
<FormControl
type="text"
name="price"
value={this.state.price}
onChange={this.updateItem}
/>
.....
)
}
}
This worked fine for updating the value of the of the inputs, however the state was local to the item and not reflected or accessible by the form
I am trying to figure out how to keep the state in the form and have the item update the state of the form
I think this is the right approach but I can't figure out how to get it to work.
At this point I have something similar the following:
class Form extends Component{
this.state = {
items: [
{ name: 'soup', price: 7, quantity: 1 }
{ name: 'salad', price: 5, quantity: 2 }
]
}
updateItem(e) {
// Not sure how to handle updating
}
removeItem(item) {
let items = this.state.items;
items.splice(items.indexOf(item), 1);
this.setState({items: items})
}
render(){
return(
<ItemTable items={this.state.items} updateItem={this.updateItem} removeItem={this.removeItem} />
)
}
}
ItemTable:
class ItemTable extends Component {
removeItem(item){
this.props.removeItem(item)
}
render(){
let items = [];
this.props.items.forEach((item) => {
items.push(<ItemRow item={item} key={item.id} removeItem={this.removeItem.bind(this,item)} updateItem={this.props.updateItem}/>);
});
return(
{items}
)
}
}
ItemRow:
class ItemRow extends Component {
removeItem(item){
this.props.removeItem(item)
}
render() {
return (
<FormControl
type="text"
name="name"
value={this.props.item.name}
onChange={this.updateItem}
/>
<FormControl
type="text"
name="quantity"
value={this.props.item.quantity}
onChange={this.updateItem}
/>
<FormControl
type="text"
name="price"
value={this.props.item.price}
onChange={this.updateItem}
/>
<Button bsStyle="warning" onClick={this.removeItem}><Glyphicon glyph="trash"/></Button>
)
}
}
You're very close to the solution.
If you need to have a state shared between components, you should have it in the most parent component that should be aware of the state (in your case the Form component).
You pass down as props the method "updateItem" from the Form to the ItemTable and then ItemRow (like you're doing)
At this stage, inside the ItemRow you can use the method by calling 'this.props.updateItem' and you can run the function defined in Form, passing some parameters, if you need to.

Categories