React Redux setState after action is dispatched - reactjs

This is my EditStudent component in React where I am using Redux for state management. This is my code of the component.
import React, { Component } from 'react'
import {connect} from 'react-redux';
import {getStudentDetail} from '../actions/studentActions';
class EditStudents extends Component {
constructor(props){
super(props);
this.onInputChange = this.onInputChange.bind(this);
this.state = {
name:this.props.student.name,
email:this.props.student.email,
address:this.props.student.address
}
// this.state
}
render() {
return (
<div>
<form>
<p>
<label>Name</label>
<input type="text" name="name"
value={this.state.name}
onChange={this.onInputChange}
/>
</p>
<p>
<label>Email</label>
<input type="text" name="email"
value={this.state.email}
onChange={this.onInputChange}
/>
</p>
<p>
<label>Address</label>
<input type="text" name="address"
value={this.state.address}
onChange={this.onInputChange}
/>
</p>
<button type="submit">Update</button>
</form>
</div>
)
}
onInputChange(e){
var name = e.target.name;
this.setState({[name]:e.target.value});
}
componentDidMount(){
this.props.getStudentDetail(this.props.studentId());
}
}
let mapDispatchToProps = (dispatch,ownProps)=>{
return {
getStudentDetail: (id)=>{
dispatch(getStudentDetail(id));
},
studentId: ()=>{
return ownProps.match.params.id;
}
}
}
const mapStateToProps = (state)=> {
return {
student:state.students.student
}
}
export default connect(mapStateToProps,mapDispatchToProps)(EditStudents);
Here I am having the problem displaying ajax response in text input field. I am setting component in the constructor but when ajax request completes state is not set.
Is it possible to set state after ajax completes?

I solve it by adding componentWillReceiveProps() function on component.
componentWillReceiveProps(nextProps){
this.setState({
name:nextProps.student.name,
email:nextProps.student.email,
address:nextProps.student.address
})
}
Thanks so much, Sakhi Mansoor for the help.

you need to store input value in your state as you're re-rendering your input so changes are not getting reflected.
Do the folowing changes:
constructor(props){
super(props);
this.onInputChange = this.onInputChange.bind(this);
this.state = {
email:this.props.student.email,
address: this.props.student.address
}
}
In you render:
<p>
<label>Email</label>
<input type="text" name="email" value={this.state.email} onChange={this.onInputChange('name)}/>
</p>
<p>
<label>Address</label>
<input type="text" name="address" value={this.state.address} onChange={this.onInputChange('name')}/>
</p>
onInputChange= name => event => {
this.setState({
[name]: event.target.value,
});
};

There are two ways
First -
constructor(props) {
super(props);
this.state = {
firstName: ""
};
}
componentDidMount() {
this.props.userProfile(); // Redux Action
}
componentWillReceiveProps(nextProps) {
console.log(nextProps);
this.setState({
firstName: nextProps.user.firstName
});
}
Second -
constructor(props) {
super(props);
this.props.userProfile(); // Redux Action
this.state = {
firstName: ""
};
}
static getDerivedStateFromProps(props, state) {
return {firstName: this.props.user.firstName };
}

Related

Add data to the page without refreshing it in React

I've posted this question earlier, but probably not quite clearly formulated it. I have a chat. When I add message it goes to database but not updated on page. So I need it to be updated on page after adding a msg.
Parent component Forms
export default class Forms extends Component {
constructor() {
super()
this.state = {
messages: [],
}
this.sendMessage = this.sendMessage.bind(this);
}
componentDidMount(){
client1.getEntries({limit:300, order: 'sys.createdAt',
content_type:'nameTest'}).then(response => {
this.setState({messages: response.items});
}).catch(e => {
console.log(e);
});
}
sendMessage(data) {
client2.getSpace(client2.space)
.then((space) => space.getEnvironment('master'))
.then((environment) => environment.createEntry('nameTest', {
fields: {
chatName: {
'en-US': data.get('chatName')
... some data
}
}))
.then((entry) => entry.publish())
.catch(console.error)
}
render() {
return (
<div className="chat">
<div className="container-xl">
<MessageList messages={this.state.messages}/>
<SendMsg onSendMessage={this.sendMessage}/>
</div>
</div>
);
}
}
the child component SengMsg
export default class SendMsg extends Component {
constructor() {
super()
this.state = {
message:'',
userEmail:'ddd#gmail.com',
chatName:'ggg'
}
this.sendMessage = this.sendMessage.bind(this)
this.handleChange = this.handleChange.bind(this)
}
handleChange(e) {
this.setState({
message: e.target.value,
})
}
sendMessage(e) {
e.preventDefault();
const { onSendMessage } = this.props;
const form = e.target;
const data = new FormData(form);
// if send message handler was passed, invoke with form data
onSendMessage && onSendMessage(data);
this.setState({
message: ''
})
}
render() {
return (
<div className="send-message">
<Form className="send-msg" onSubmit={this.sendMessage}>
<FormGroup>
<Input type="hidden" name="userEmail" value={this.state.userEmail}/>
</FormGroup>
<FormGroup>
<Input type="hidden" name="chatName" value={this.state.chatName}/>
</FormGroup>
<FormGroup>
<Input
type="text"
name="text"
onChange={this.handleChange}
value={this.state.message}
placeholder="Write your message here"
required />
</FormGroup>
<FormGroup>
<Input type="hidden" name="dateCreated" value={moment().format()} onChange={this.handleChange}/>
</FormGroup>
</Form>
</div>
);
}
}

How to pass Child Component's form value to Parent Component

Is there a way to pass form data from child component to Parent component, where submit buttons have been kept in parent component.
NOTE: - I don't want to use ref for this as the ref would be waste of so much of memory and I might have 6-7 children in the parent.
I have created a similar situation to show what I am stuck on.
class FirstChildForm extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
}
}
render() {
return (
<div className="form">
<input type="text" placeholder="Enter your name..." />
<input type="password" placeholder="Enter password" />
</div>
)
}
}
class SecondChildForm extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
}
}
render() {
return (
<div className="form">
<input type="text" placeholder="Enter your name..." />
<input type="password" placeholder="Enter password" />
</div>
);
}
}
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
}
}
handleSubmit = () => {
}
render() {
return (
<div className="parent">
<FirstChildForm />
<SecondChildForm />
<button onClick={this.handleSubmit}> Submit</button>
</div>
)
}
}
Sure, the concept is called lifting the state up. Basically, your <App /> component would hold the data from both components. I'm going to simplify a bit, but you should understand what I'm doing.
FirstChildForm.js
<input type="text" name="username" onChange={e => props.updateData('user', e.target.value)}
SecondChildForm.js
<input type="password" name="password" onChange={e => props.updateData('pass', e.target.value)}
App.js
export default class App extends React.Component {
constructor() {
super();
this.state = {
user: '',
pass: '',
};
}
handleSubmit = () => {};
updateData = (target, value) => {
this.setState({ [target]: value });
};
render() {
return (
<div className="parent">
<FirstChildForm updateData={this.updateData} />
<SecondChildForm updateData={this.updateData} />
<button onClick={this.handleSubmit}> Submit</button>
</div>
);
}
}
The <App /> component is the source of truth. Please note:
By lifting the state up, <FirstChildForm /> and <SecondChildForm /> don't need to be class based components anymore, they can be functional components. If you want them to remain class based for whatever reason, change props.updateData to this.props.updateData, else it won't work.
The parent is where we define the function, the child is where we execute it, sending data to parent, basically!
by passing a function to the child component as props and passing the child component's state as parameter to the function
as i dont know what you exactly want to code inside but only to understand checkout
following example
parent:
export default class App extends React.Component {
constructor(props){
super(props);
this.state = {
data: []
}
}
handleSubmit = () => {
}
handleData = (newData) => {
this.setState({data: newData});
}
render(){
return (
<div className="parent">
<FirstChildForm / >
<SecondChildForm onSelectData={this.handleData}/>
<button onClick={this.handleSubmit}> Submit</button>
</div>
)
}
}
Child:
class SecondChildForm extends React.Component{
constructor(props){
super(props);
this.state = {
data:'hello'
}
}
handleDataChange: function () {
var newData = this.state.data
this.props.onSelectData(newData);
},
render(){
return (
<div className="form">
<input type="text" placeholder="Enter your name..." />
<input type="password" placeholder="Enter password" />
<button onclick={this.handleDataChange}>submit</button>
</div>
);
}
}
You will have to pass a function down to your children components. Your children will now be able to bind this function from their props to the given fields.
class FirstChildForm extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
}
}
render() {
return (
<div className="form">
<input type="text" placeholder="Enter your name..." onChange={this.props.dataChanged('name')}/>
<input type="password" placeholder="Enter password" onChange={this.props.dataChanged('password')}/>
</div>
)
}
}
class SecondChildForm extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
}
}
render() {
return (
<div className="form">
<input type="text" placeholder="Enter your name..." onChange={this.props.dataChanged('name')}/>
<input type="password" placeholder="Enter password" onChange={this.props.dataChanged('password')}/>
</div>
);
}
}
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
}
}
handleChange = form => field => ev => {
this.setState(prev => ({ [form]: { ...prev[form], [field]: ev.target.value } }))
}
The resulting state of your parent will have the following structure :
{
'firstForm': {
'name': '',
'password': ''
},
'secondForm': {
'name': '',
'password': ''
}
}

React updating form fields from backend

I am working on a project using React and Redux where I need to update the data from the back end in the form. Since the data that needs to be updated will be fetched from the backend, I am initializing component state from props and the form values will be assigned from component state. The issue that I am having is that some of the values I am fetching from the back end are empty(undefined) at first and need to be added from the form and that is throwing error when the component is rendered. Please refer my React class below, any suggestion on how to avoid the component from throwing an error if the props value is undefined is appreciated.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import Radium from 'radium';
import ProfileBio from '../common/commonProfileBio';
const form = reduxForm({
form: 'profileEdit',
fields: [ 'firstName', 'lastName', 'email', 'educations'],
// validate
});
class ProfileBioList extends Component {
constructor(props) {
super(props);
this.state = {
editing: false,
//this is throwing error since the values are undefined initially
firstName: this.props.data.bio.firstName,
lastName: this.props.data.bio.lastName,
email: this.props.data.email
};
this.toggleEditing = this.toggleEditing.bind(this);
this.handleUpdate = this.handleUpdate.bind(this);
this.onChange = this.onChange.bind(this);
}
getStyles() {
return {
ulStyles: {
listStyle: "none"
},
listItem: {
borderRadius: 0,
boxShadow: "1px 1px 1px lightgrey"
}
}
}
handleUpdate(e) {
e.preventDefault();
this.props.updateProfile({bio:this.state});
}
renderItemOrEditField() {
const styles = this.getStyles();
if(this.state.editing !== true){
return (
<ProfileBio
toggleEditing={this.toggleEditing}
data={this.props.data}
styles={styles}/>
);
} else {
return (
<div className="row ">
<div className="col-xs-10 col-xs-offset-1">
<form>
<ul className="list-group-item" style={styles.ulStyles}>
<li >
<label>First Name:</label>
<input
name="firstName"
type="text"
className="form-control"
value={this.state.firstName}
onChange={this.onChange}/>
</li>
<li className="">
<label className="">Last Name:</label>
<input
name="lastName"
type="text"
className="form-control"
value={this.state.lastName }
onChange={this.onChange}/>
</li>
<li className="">
<label>Email:</label>
<input
name="email"
type="text"
className="form-control"
value={this.state.email}
onChange={this.onChange}/>
</li><br />
<li className="btn-group">
<button
onClick={this.handleUpdate}
action="submit"
className="btn btn-primary">Update</button>
<button
onClick={this.toggleEditing}
className="btn btn-default">Cancel</button>
</li>
</ul>
</form>
</div>
</div>
);
}
}
toggleEditing(e){
e.preventDefault();
this.setState({editing: !this.state.editing});
}
render(){
return (
<div>
{this.renderItemOrEditField()}
</div>
);
}
}
export default connect(null)(form(ProfileBioList));
The easiest way would be to use ES6 destructuring assignment with default value. So your constructor would look something like this...
class ProfileBioList extends Component {
constructor(props) {
super(props);
const {
bio: {
firstName = '',
lastName = '',
} = {},
email = '',
} = props.data;
this.state = {
editing: false,
firstName: firstName,
lastName: lastName,
email: email
};
this.toggleEditing = this.toggleEditing.bind(this);
this.handleUpdate = this.handleUpdate.bind(this);
this.onChange = this.onChange.bind(this);
}
...
}

Handling multiple onChange callbacks in a React component

This is how I'm currently handling the scenario with two input boxes. As a separate update method for each one. Can/should this be done with a single handleChange method instead?
https://codepen.io/r11na/pen/bZKOpj?editors=0011
class App extends React.Component {
constructor(props) {
super(props);
this.handleChange1 = this.handleChange1.bind(this);
this.handleChange2 = this.handleChange2.bind(this);
this.state = {
name1: '',
name2: ''
};
};
handleChange1(e) {
this.setState({
name1: e.target.value
});
};
handleChange2(e) {
this.setState({
name2: e.target.value
});
};
render() {
return (
<div class="row column">
<Label name={this.state.name1}/>
<Input onChange={this.handleChange1} />
<Label name={this.state.name2}/>
<Input onChange={this.handleChange2} />
</div>
);
};
}
const Label = props => (
<p {...props}>Hello: <span className="label-name">{props.name}</span></p>
);
const Input = props => (
<input placeholder="Enter your name" {...props} type="text" />
);
ReactDOM.render(<App />, document.getElementById('app'))
Can/should this be done with a single handleChange method instead?
You can simplify your code like so.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name1: '',
name2: ''
};
};
handleChange(e, name) {
this.setState({ [name]: e.target.value });
};
render() {
return (
<div class="row column">
<Label name={this.state.name1}/>
<Input onChange={ (e) => this.handleChange(e, 'name1') } />
<Label name={this.state.name2}/>
<Input onChange={ (e) => this.handleChange(e, 'name2') } />
</div>
);
};
}
Example
Thanks #Alik that mentioned about eslint rule jsx-no-bind,
A bind call or arrow function in a JSX prop will create a brand new
function on every single render. This is bad for performance, as it
will result in the garbage collector being invoked way more than is
necessary.
Following this rule you can change your code like
class App extends React.Component {
constructor(props) {
super(props);
this.onChange = {
name1: this.handleChange.bind(this, 'name1'),
name2: this.handleChange.bind(this, 'name2'),
}
this.state = {
name1: '',
name2: ''
};
};
handleChange(name, event) {
this.setState({ [name]: event.target.value });
};
render() {
return (
<div class="row column">
<Label name={this.state.name1}/>
<Input onChange={ this.onChange.name1 } />
<Label name={this.state.name2}/>
<Input onChange={ this.onChange.name2 } />
</div>
);
};
}
Example

React: Input field doesn't receive input

I tried to type in form input field, but its not working. Currently its in readonly mode, I am not sure why . Here is the component :
import React from 'react';
export default class SearchBar extends React.Component {
constructor(props) {
super(props);
this.state = { term: ''};
this.onInputChange = this.onInputChange.bind(this);
}
onInputChange(event) {
console.log(event.target.value);
this.setState({ term: event.target.value});
}
render() {
return (
<form className="input-group">
<input
placeholder="Give a five day forecast"
className="form-control"
value={this.state.term}
onChange={this.noInputChange}
/>
<span className="input-group-btn">
<button type="submit" className="btn btn-secondary"> Submit</button>
</span>
</form>
)
}
}
onChange={this.noInputChange}
should be
onChange={this.onInputChange}

Resources