How to push values into state by calling single onChange function - react - reactjs

I am new to reactive. I am working on react+flux+alt with ES6.
I have a form for creating new record.
Component
import React from 'react';
import { Input, Button, Glyphicon, ButtonToolbar } from 'react-bootstrap';
import AttributeSectionStore from 'stores/attributeSection/AttributeSectionStore';
import TextBoxesSet from '../descriptionTextBoxes';
import styles from 'scss/_common';
export default class AttributeSection extends React.Component {
constructor(props) {
super(props);
}
_onCreate = () => {
console.log('___________', this.state);
}
onChangeName = (evt) => {
this.setState({name: evt.target.value});
};
onChangeRank = (evt) => {
this.setState({rank: evt.target.value});
};
static getPropsFromStores() {
return recordStore.getState();
}
render() {
return (
<div className="container">
<div className={styles.mainheader}>
<h2 >New Record</h2>
</div>
<div className="col-md-9">
<form className="form-horizontal">
<div className="row">
<div className="col-md-12">
<Input type="text" label="Name" labelClassName="col-xs-2"
wrapperClassName="col-xs-4" value={this.props.name}
onChange={this.onChangeName}/>
</div>
</div>
<div className="row">
<div className="col-md-12">
<Input type="number" label="Rank" labelClassName="col-xs-2"
wrapperClassName="col-xs-4" value={this.props.rank}
onChange={this.onChangeRank}/>
</div>
</div>
<div className="row">
<div className="col-md-4 col-md-offset-2">
<ButtonToolbar className={styles.formBtnGrp}>
<Button bsStyle="primary" onClick={this._onCreate}>Create</Button>
<Button type="reset">Cancel</Button>
</ButtonToolbar>
</div>
</div>
</form>
</div>
</div>
);
}
}
AttributeSection.propTypes = {
name: React.PropTypes.string
rank: React.PropTypes.number
};
Using above component now I'm getting data into state but form may have more than 2 fields. I'm using two functions to update state instead of that how can use single function to update state object?Is there any other best practice is there?

The most common pattern to solve this is using bind() to curry a value to the onchange callback. This is was #knowbody referenced (React.js: Identifying different inputs with one onChange handler)
An alternate, but similar, pattern is adding a second tag within the element to identify the name of the state property to change. I'll show an example using label from your code (obviously you want to use a dedicated tag since label is for display and would be localized).
onInputChanged(evt) {
var newState = this.state,
propName = evt.target.label.toLowerCase();
newState[propName] = evt.target.value;
this.setState(newState);
};

Related

ReactDOM.render() is not working when called again after making the changes in template

The render() function is working fine on the initial call. But when I call it again, via one of the event handler function, it doesn't work. The chrome dev. tools console is not showing any errors at all.
The changes are made on the options array and then I have to display them as list items in the Unordered list.
Sorry for the bad English.
let options = ["example"];
let t =
<div>
<h1>indicition app</h1>
<p>Put your life into the hands of the computer</p>
<p>Here are your options</p>
<ul>
{options.map(text => <li key={text}>{text}</li> )} //Changes occur here but not showing.
</ul>
<form onSubmit={submit}>
<input type="text" name="input" />
<button>Add Item</button>
</form>
</div>;
function submit(e)
{
e.preventDefault();
if (!e.target.elements.input.value) return;
options.push(e.target.elements.input.value);
console.log(options);
ReactDOM.render(t, document.getElementsByTagName("div")[0]); // Here it is not working.
}
ReactDOM.render(t, document.getElementsByTagName("div")[0]); //Here it works for once.
Render function is automatically called when your state has changed. To do that, create a new class and save your options in the state. After that append your options and save them in the state, using this.setState. See the example:
import React, { Component } from 'react';
export default class MyClass extends Component {
state = {
options: ['example'],
};
render() {
return (
<div>
<h1>indicition app</h1>
<p>Put your life into the hands of the computer</p>
<p>Here are your options</p>
<ul>
{this.state.options.map((text) => (
<li key={text}>{text}</li>
))}
//Changes occur here but not showing.
</ul>
<form onSubmit={this.submit}>
<input type='text' name='input' />
<button>Add Item</button>
</form>
</div>);
}
submit = (e) => {
e.preventDefault();
if (!e.target.elements.input.value) return;
let { options } = this.state;
options = options.concat(e.target.elements.input.value);
this.setState({ options });
};
}
ReactDOM.render(MyClass, document.getElementsByTagName("div")[0]); //Here it works for once.

React state is udpate but not in the css doodle tag

The state of the app is ok. It is updating when I change a value in the textarea I can see the changement in the state component with the react utility but the css doodle don't update. I must refresh manually to see the changes I don't understand why. Thanks a lot
class App extends Component {
state ={
dood: doodText
}
componentDidMount(){
const dood=localStorage.getItem('dood')
if(dood){
this.setState({dood})
}
else{
this.setState({dood: doodText})
}
}
componentDidUpdate(){
const {dood}= this.state
localStorage.setItem('dood', dood)
}
handleChange = event =>{
var dood= event.target.value
this.setState({dood})
}
render(){
return (
<div className="container" onChange={this.handleChange} >
<div className="row">
<div className="col-sm-6">
<textarea onChange={this.handleChange} value={this.state.dood}
className="form-control"
rows="25" />
</div>
</div>
<div className="col-sm-6" onChange={this.handleChange} >
<css-doodle >{this.state.dood}</css-doodle>
</div>
<div>
</div>
</div>
);
}
}
export default App;
Just set some order
I think its should work, I add a div with dood inside to see if its work.
And I write some comment for you.
class App extends Component {
constructor() {
super();
this.handleChange = this.handleChange.bind(this);
}
state = {
dood: doodText
}
componentDidMount() {
const dood = localStorage.getItem('dood')
if (dood) {
this.setState({ dood })
}
// THIS ELSE DO NOT NECESSARY
// else {
// this.setState({ dood: doodText })
// }
}
componentDidUpdate() {
// FOR WHY IS THAT HAPPEN EVERY UPDATE?
const dood = this.state.dood
localStorage.setItem('dood', dood)
}
// USE BIND IS BETTER
handleChange(ev) {
var dood = ev.target.value
this.setState({ dood })
}
render() {
return (
<div className="container" >
<div className="row">
<div className="col-sm-6">
<textarea onChange={this.handleChange} value={this.state.dood}
className="form-control"
rows="25" />
</div>
</div>
<div>{dood}</div>
<div className="col-sm-6" >
<css-doodle >{this.state.dood}</css-doodle>
</div>
</div>
);
}
}
export default App;
css-doodle provides an .update() method to manually update it, see:
https://css-doodle.com/#js-api-update
So you can listen to the change or input event of the textarea and then call .update()

Value of Form Input not updating on Submit in React

I am new to React and writing a basic program where using two input fields and a button I want to show the submitted data through another component.
I have declared state in the App component and used a handleChange and handleSubmit method. I have used this state data as props in Display component. But I am getting the data shown when input changes and not on submit.
Have a look at my code:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(){
super();
this.state={
first:'',
last:''
}
this.handleChange=this.handleChange.bind(this);
//this.handleSubmit=this.handleSubmit.bind(this);
}
//handleChange method will capture the change in the values of input field
//Here [e.target.name]:e.target.value will set the input value to name="first" and name="last"
handleChange(e){
this.setState({
[e.target.name]:e.target.value
});
}
handleSubmit(e){
e.preventdefault();
this.handleChange();
}
render() {
return (
<div className="App">
<div class="row">
<input name="first" onChange={this.handleChange}type="text" value={this.state.first}></input>
</div>
<div class="row">
<input name="last" onChange={this.handleChange}type="text" value={this.state.last}></input>
</div>
<div class="row">
<input name="submit" type="button" onSubmit={this.handleSubmit}></input>
</div>
<Display name={this.state.first} last={this.state.last}/>
</div>
);
}
}
const Display=(props)=>{
return(
<div>
<div class="row">
{props.name}
</div>
<div class="row">
{props.last}
</div>
</div>
)
}
export default App;
Also can somebody explain me why do we write [e.target.name]:e.target.value
in setState and why do we right it as []?
The handleChange function that you have used sets the state to first and last states respectively when they change. This pattern is called Controlled Components in React.
On why we use [] in the handleChange function, as you have already pointed out in comments of your code, it is to set the state to first and last, which are also name properties of your inputs. This syntax is called Computed Property and you can find explanation on this in React docs.
If you want the Display component to pick up the state only when you press submit, the alternative is to maintain two separate states for them. One is for the form and another one is for the validated one that is displayed.
Demo:
const { Component } = React;
class App extends Component {
constructor(){
super();
this.state={
first:'',
last:''
}
this.handleSubmit=this.handleSubmit.bind(this);
}
handleSubmit(first, last){
this.setState({
first,
last,
})
}
render() {
return (
<div className="App">
<Form onSubmit={this.handleSubmit} />
<Display name={this.state.first} last={this.state.last}/>
</div>
);
}
}
class Form extends Component {
constructor(){
super();
this.state={
first:'',
last:''
}
this.handleChange=this.handleChange.bind(this);
this.handleSubmit=this.handleSubmit.bind(this);
}
handleChange(e){
this.setState({
[e.target.name]:e.target.value
});
}
handleSubmit(e) {
e.preventDefault();
this.props.onSubmit(this.state.first, this.state.last);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className="row">
<input name="first" onChange={this.handleChange}type="text" value={this.state.first} />
</div>
<div className="row">
<input name="last" onChange={this.handleChange}type="text" value={this.state.last} />
</div>
<div className="row">
<input name="submit" type="submit" />
</div>
</form>
);
}
}
const Display=(props)=>{
return(
<div>
<div className="row">
{props.name}
</div>
<div className="row">
{props.last}
</div>
</div>
)
}
ReactDOM.render(<App />, document.getElementById("root"));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>

react render --How to render 2 props with map function in discussion app

having trouble with dicussion app. I can render only header or post at a given time.Having trouble how to render in thread.jsx.
This app is in react and firebase.
Pls suggest me how to fix this.
I think map function in post is not right.how to write a object with map function. do I need to write object as a prop?
thread.jsx--------
postEditor.jsx-----
import React, {Component} from 'react';
import './DpostEditor.css';
export default class PostEditor extends Component {
constructor(props) {
super(props);
this.state= {
newPostBody:'',
newHeaderBody:'',
};
this. handlePostHeaderInputChange=this.handlePostHeaderInputChange.bind(this);
this.handlePostEditorInputChange = this.handlePostEditorInputChange.bind(this);
this.createPost = this.createPost.bind(this);
}
handlePostEditorInputChange(e) {
this.setState({
newPostBody : e.target.value,
})
}
handlePostHeaderInputChange(e) {
this.setState({
newHeaderBody : e.target.value,
})
}
createPost() {
this.props.addPost(this.state.newPostBody);
this.setState({
newPostBody: '',
newHeaderBody:'',
});
}
render() {
return(
<div className= "panel panel-default post-editor">
<div className="panel-body">
<textarea className= "form-control post-header-input" value={this.state.newHeaderBody} onChange={this. handlePostHeaderInputChange }/>
<textarea className= "form-control post-editor-input" value={this.state.newPostBody} onChange={this.handlePostEditorInputChange }/>
<button className= "btn btn-success post-editor-button" onClick= {this.createPost}> Post </button>
</div>
</div>
)
}
}
Post.jsx------
import React from 'react';
import './Dpost.css';
const Post= (props)=> (
<div className=" panel panel-default post-body">
<div className="panel-heading">
{props.postBody.map((header,postType,idx)=>(
<div>
<div> {header} </div>
<div> {postType} </div>
</div>
))
}
</div>
</div>
);
export default Post;
Exactly the map function is wrong. You're passing 3 parameters that don't match the ones the map array helper uses.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
Scroll to the Parameters section to check them. Right now you're passing the parameters of the post and not the array.
Perhaps you should try something like this:
{props.postBody.map((post)=>(
<div key={post.idx}>// remember the key in a map iterator
<div> {post.header} </div>
<div> {post.postType} </div>
</div>
))}
But this depends on the structure of your props.postBody array. The code above assumes that is a collection of posts.
Edit
Considering the fact that your props may be undefined at some point you use a conditional logic to run the array map helper:
{props.postBody ? props.postBody.map((post)=>(
<div key={post.idx}>// remember the key in a map iterator
<div> {post.header} </div>
<div> {post.postType} </div>
</div>
)) : null}

React - get value of input on click

I'm pretty new to React. I have a simple app where text is entered into an input field and then sent to a server when a button is clicked. How can I get the value of the input field? This is incredbily easy in jQuery but I can't figure it out with React.
Here's my code:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props){
super(props)
this.state = {
terms: ''
}
this.updateTerms = this.updateTerms.bind(this);
}
updateTerms(evt) {
this.setState({
terms: evt.target.value
})
}
search() {
alert(this.state.terms)
}
render() {
const btn1 = {
width: '100%',
backgroundColor: 'white'
}
return (
<div className= 'wrapper'>
<div className= 'item1'>
Specials Fact Tree Demo
</div>
<div className= 'item2'>
Search Term(s):<br/>
<input className= 'form-control' type="text" onChange={this.updateTerms} />
<br/>
<div id = 'results' >
<div id='resultsWrap' >
<select className= 'form-control' id= 'styles' ></select>
<select className= 'form-control' id= 'similar' ></select>
<div id= 'attsDiv' className= 'form-control' >
<dl id= 'atts'></dl>
</div>
</div>
</div>
<button className="btn btn-default" style = {btn1} id= 'search' onClick={this.search}>Search</button>
<div id="activeTraining" >
</div>
</div>
</div>
);
}
}
export default App;
Any help would be greatly appreciated. Plus, easy points!
You forgot to bind the search method as well (inside the constructor):
this.search = this.search.bind(this);
If you are already familar with ECMAScript 6 you can just use arrow functions and save on the bindings like:
search = () => {
alert(this.state.terms)
}

Resources