I want to write a tool which iterates through the pupils of a school class. So I have a text field in a react component which displays a value:
<input className="form-control" onChange={this.handleInputChange} value={this.props.activePupil.name}></input>
If I tie its value to pupilName (which is given to my component as a prop, by my Redux store) it always show me the right value, whenever the value in the store changes.
However, I also want the user to be able to changel the pupil's name should it be inaccurate. This does not work if the value of the input field is tied to the props.
So I thought I would tie it to some local state, and in the constructor I do:
this.state = {
inputField: "No Pupil selected yet."
};
...and whenever the user edits the textfield, this wil be handled in a handleInputChange method, like so:
this.setState({
inputField: evt.target.value
});
This all works fine, but now when I want to iterate through the school class, my reducer, which contains the current or active pupil is correct (console.log shows the correct value), but the value displayed in the input field is always one behind.
Why could this be?
edit: full code:
// Import React
import React from 'react';
class PupilViewer extends React.Component{
constructor(props) {
super(props);
this.state = {
inputField: "No Pupil selected yet."
};
this.handleInputChange = this.handleInputChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
handleInputChange(evt) {
this.setState({
inputField: evt.target.value
});
}
onSubmit(e) {
e.preventDefault();
this.props.nextPupil();
this.state.inputField = this.props.activePupil.name;
}
render() {
// Return JSX via render()
return (
<div className="">
<h1>Pupil View</h1>
<input className="form-control" onChange={this.handleInputChange} value={this.state.inputField}></input>
<button className="btn btn-large btn-positive" onClick={this.onSubmit}>Next</button>
</div>
);
}
}
// Export Search
export default PupilViewer
The above component is giving the following arguments from the container:
<PupilViewer nextPupil={this.props.nextPupil} pupilList={this.props.pupils} activePupil={this.props.activePupil} saveChanges={this.props.saveChanges} updateActivePupil={this.props.updateActivePupil}/>
I need to point out here that nextPupil, saveChanges & updateActivePupil are functions which I pass down here. I have two reducers. One contains a object with all pupils, another the active pupil:
active pupil reducer:
export default function (state={id: -1, name:"Default Pupil Name in State"}, action) {
switch (action.type) {
case "UPDATE_ACTIVE":
let newState = action.payload;
return newState;
case "GET_ACTIVE":
return state;
default:
return state;
}
}
my reducer with all pupils:
//my default array
let default_pupils = [{
id: 0,
name: "Matthew",
//there are more attributes here
},
{
//and there are more pupils here
}];
export default function (state=default_pupils, action) {
switch (action.type) {
case "SAVE_CHANGES":
let newState = [...state];
newState[2] = {...newState[2], name: action.payload.name };
return newState;
default:
return state;
}
}
Lastly, here are my actions:
var activePupilCount = 0;
export const getActivePupil = () => {
return {
type: "GET_ACTIVE"
};
}
export const updateActivePupil = (pupil) => {
console.log("UPDATE:" + pupil.name)
return {
type: "UPDATE_ACTIVE",
payload: pupil
};
}
export const nextActivePupil = () => {
return (dispatch, getState) => {
const {activePupil, pupilList} = getState();
activePupilCount++;
console.log("COUNT:"+ activePupilCount);
const nextIndex = (activePupilCount) % pupilList.length;
const nextActive = pupilList[nextIndex];
dispatch({ type: "UPDATE_ACTIVE", payload: nextActive });
}
};
export const saveChanges = (pupil) => {
return {
type: "SAVE_CHANGES",
payload: pupil
}
};
Not sure if this will fix everything and make it work as expected, but you should never do
this.state.inputField = this.props.activePupil.name;
instead, do
this.setState({ inputField: this.props.activePupil.name })