React updating props from state - reactjs

I have a form inside a list.
The list passes a object with a array of topics (another object) on props to the child form, and when I change the values on the state the props values are changed also.
I would like to maintain props in case the user cancels the changes.
constructor(props: IMinuteProps) {
super(props);
this.state = {
meeting: this.props.meeting,
EditingThemeDeliberation: undefined
};
}
After Changing the field event:
(EditingThemeDeliberation is the topic being changed)
private UpdateDeliberation(event) {
let { Reunion, EditingThemeDeliberation} = this.state;
EditingThemeDeliberation.Deliberation = event.target.value;
Reunion.Themes.splice(Reunion.Themes.findIndex(i => i.Id ===
this.state.EditingThemeDeliberation.Id), 1,EditingThemeDeliberation);
// Previous command changes both props and state
this.setState({ EditingThemeDeliberation: undefined, Reunion: Reunion
});
}

Related

React doesn't re-render component on state change

I have a component with a search field and a list of items, where I am using that value from search field to filter through the array of items and update the list. This is are the parts of the component responsible for that:
this.state = {
tasks: null,
unfilteredTasks: null,
};
componentDidMount() {
this.gateway.loadTasks().then(result => {
this.setState({tasks: result, unfilteredTasks: result})
})
}
onSearchChange(event) {
const value = event.target.value;
const propsToCheck = ['task.text', 'type', 'status', 'topic.name'];
this.setState(prevState => {
const tasks = prevState.unfilteredTasks.filter(obj => checkObjectContainsValue(obj, value)(propsToCheck));
return {tasks: tasks};
})
}
render() {
return <TasksPresentation
<TasksList
tasks={this.state.tasks}
onSearchChange={(event) => this.onSearchChange(event)}>
</TasksList>
</TasksPresentation>
}
With this I can see in the developer tools that the state of the component is being changed and that the tasks list is being filtered, but the lists is not being re-rendered. Why is that, how can I fix this?
Update
On inspecting further down the component chain. I can see that the tas list table component that is responsible for rendering of the list is a class component that uses prop rows which is the tasks list being send down from the parent component, to build state:
constructor(props) {
super(props);
this.state.data = this.transformRows(props.rows)
}

React- How to update child prop based on parent state

I'm running into a problem getting a child react component to update when its parent stage changes. I have an Editor parent component that sets its state and then updates the state if the component receives an updated schedule (from a graphQL mutation component).
The problem is that componentDidUpdate triggers which does trigger the Modefield to update, but it is before the setState in componentDidUpdate can update the state. This means the child doesn't update. (Note- I know a more idiomatic way is to get rid of state all together, but this way allows a field to both edit and create a new one.)
How can I cause the child to update based on the parent's state change?
export const updateScheduleMutation = gql`
mutation updateScheduleMutation(
$id: ID!
$mode: String
) {
updateSchedule(
id: $id
mode: $mode
) {
id
mode
}
}
`;
class EditorWrapper extends React.Component {
constructor(props) {
super(props);
this.state = { scheduleId: props.scheduleId || '' };
}
render() {
return (
<Mutation mutation={updateScheduleMutation}>
{(updateSchedule, { mutationData }) => <Editor {...data} updateSchedule={updateSchedule} />}
</Mutation>
)
}
}
class Editor extends React.Component {
constructor(props) {
super(props);
const { schedule } = props;
if(schedule === null){
this.state = {
schedule: { mode: schedule.mode || "" }
};
}
}
componentDidUpdate(prevProps) {
if (prevProps.schedule !== this.props.schedule) {
this.setState({ ...this.props.schedule });
}
}
changeInput = (path, input) => {
const { updateSchedule, schedule } = this.props;
const field = path.split('.')[1];
updateSchedule({ variables: { id: schedule.id, [field]: input } });
this.setState({ [path]: input });
};
render() {
return (
<ModeField input={this.state.schedule.input} />
);
}
}
const ModeField = ({input}) => FormControl value={input} />
EDIT: I updated the component to show the higher level graphQL wrapper. The reason why I wanted state in the Editor component is that in the event the graphQL query comes back as null, I set this.state.mode to an empty string, which I then update on change. Then, I would create a new schedule item on submit.
LIFT THE STATE UP! Try to manage the base state of your data in parent component and use the data as props in your component:
You also can try getDerivedStateFromProps, but before check the react blog advices:
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html

ReactJS change value of nested state

I have a form in which inputs have an initial value (which is different for each input). I set the value by saving the data in a state array, like so:
function createData(header, content) {
id += 1;
return {header, content, id};
}
class ExampleForm extends React.Component {
constructor(props) {
super(props);
this.state = {
data : [
createData( Header1, Content1),
createData( Header2, Content2),
createData( Header3, Content3),
]
}
Once the user startes typing something into the input field, I need to change the value of the state in in question in the corresponding array, however, I'm having trouble correctly targeting said state.
This is what I'm currently doing:
handleInputChange = (value, target) => {
const selectedArray = {...this.state.data[target]};
selArray.header = value;
this.setState(selArray);
};
}
(Note: value is the new value, target is the index)
However, it doesn't work because when I console.log() the new value, it still returns the old value.
I also read several other questions on here (with the help of with I wrote the code for what I'm doing right now in the function that changes the value), however, that did not work.
Other questions I read include React: Update nested state? , however, I could not figure out where they got getPersonsData() from, for instance. Does anyone know what I'm doing wrong in my current code? How do I fix it?
Update the state like this:
handleInputChange = (value, target) => {
const data = [...this.state.data];
data[target].header = value;
this.setState({ data });
};
To check the updated state value, use callback method, like this:
handleInputChange = (value, target) => {
const data = [...this.state.data];
data[target].header = value;
this.setState(
{ data },
() => console.log('updated state', this.state.data)
);
};
For more details about asyn behaviour of setState check this answer: Why calling setState method doesn't mutate the state immediately?

Is Initializing state with props object causes mutation?

In my React application, one of the components needs state initialization from props.
Class ComponentA extends React.Component{
constructor(props){
this.state = {
objectA: props.objectA
}
}
someOnclickFunction(e){
let updatedObjA = this.state.objectA;
updatedObjA.value = e.target.value;
this.setState({
objectA: updatedObjA
})
}
}
In the above code snippet, props.objectA reference is copied to state. So, Am I mutating the props indirectly by updating the state?
Or setState() function will clone the object and keep new reference for the objectA?
class ComponentA extends React.Component {
constructor(props) {
super(props);
// state is null at this point, so you can't do the below.
// this.state.objectA = props.objectA
// instead, initialize the state like this:
this.state = {
objectA: props.objectA,
};
}
someOnclickFunction(e) {
// you can't set "objectA.value" like the following
// this.setState({
// objectA.value: e.target.value
// });
// you need to create a new object with your property changed, like this:
this.setState({
objectA: Object.assign({}, this.state.objectA, { value: e.target.value }),
})
}
}
This is a mistake that many beginners at react make. You can't simply update sub-properties of an object without consequences.. The following would also be wrong:
someOnclickFunction(e) {
var tmp = this.state.objectA;
// WRONG: don't do this either, you're modifying the state by doing this.
tmp.value = e.target.value;
this.setState({
objectA: tmp,
});
}
To elaborate on the correct way to do this using Object.assign.. this function takes all the parameters and merges them into the first element. So by providing a new object as the first parameter, you've created a copy of your first object with your new property.
Object.assign({}) // = {}
Object.assign({}, objectA) // = copy of objectA
Object.assign({}, objectA, { value: "newValue" }) // = copy of objectA with 'value' = 'newValue'.
Note: Object.assign() is a shallow clone, not a deep clone.

Why does it display only the last value of the array, and not the whole?

Why does it display only the last value of the array, and not the whole?.
When you update the value in the database, When you update the value in the database, it outputs all
constructor(props) {
super(props);
this.name = this.props.name;
this.state = {[this.name] : []};
}
componentDidMount() {
let cardQuantity =
firebase.database().ref("Users").child(this.name);
cardQuantity.on('value',snap => {
snap.forEach((childSnapshot)=> {
let card = {text: childSnapshot.val(), id: childSnapshot.key};
this.setState({[this.name] :[card].concat(this.state[this.name])});
});
});
}
render(){
return (
this.state[this.name].map( card => <h2 key={card.id}>{card.text}</h2>)
);
}
setState() is async so you have to use callback form of setState like below:
this.setState(prevState => ({
[this.name]: [card].concat(prevState[this.name])
}));
Demo: https://codesandbox.io/s/zrq0xxq2px
It shows 2 versions based on your code:
fixed version that is using callback form of setState and displaying the whole list
unfixed version based on your code that is showing only the last element of an array
From the official React's documentation:
If the next state depends on the previous state, we recommend using
the updater function form, instead...

Resources