M Masood
M Masood

Reputation: 167

Updating component to reflect changes in data?

Trying to create a new Note in "Create" component sent a POST request and added it to the database. Now I need the change to be reflected in the state of "App" component.

Solution: On submission of Note (Create Comp) to the database, a copy of the object is sent to App.js, where we update note property in state so that it reflects the change in the database.

Issue: Getting a few errors, problem lines are labeled in the code.

enter image description here

class App extends Component{
  state = {
    notes: []
  }

  componentDidMount(){
    fetch('http://localhost:3001/notes')
      .then(resp => resp.json())
      .then(data => this.setState({notes: data}))
  }

  onSubmitUpdateState(newNote){
    const oldState = [...this.state.notes]
    // Problem 2)
    this.setState({notes: [...oldState, newNote]});
  }
  render(){
    return(
      <div className="App">
        <Notes notes={this.state.notes}/>
        <Create updateState={this.onSubmitUpdateState}/>
      </div>
    )
  }
}

export default App;

import React, { Component } from 'react';
import classes from './Create.module.css';

class Create extends Component{
  state= {
    title: "",
    body: ""
  }
  
  onChange = (e)=>{
    this.setState({[e.target.name]: e.target.value})
  }
  handleSubmit = (e)=>{
    e.preventDefault();
    const post = {
      title: this.state.title,
      body: this.state.body
    }
    // Problem 1
    fetch('http://localhost:3001/notes', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(post)
    })
      .then(res => res.json())
      .then(data => this.props.updateState(data))
    this.setState({
      title: "",
      body: ""
    })
  }
  render(){
    return (
      <div className={classes.CreateContainer}>
        <div className={classes.Create}>
            <h1>Create</h1>
            <form onSubmit={this.handleSubmit}>
                <label>Title:</label><br/>
                <input type="text" name="title" onChange={this.onChange} value={this.state.title}></input><br/>
                <label>Body:</label><br/>
                <textarea row="8" col="50" name="body" onChange={this.onChange} value={this.state.body}></textarea><br/>
                <input type="submit" value="Submit"></input>
            </form>
        </div>
      </div>
    )
  }
} 
export default Create;

Upvotes: 0

Views: 348

Answers (4)

Joseph D.
Joseph D.

Reputation: 12174

In Javascript, class methods are not bounded by default, this SO answer talks about why is it like that?

If you're not using arrow function, make sure to bind() your event handlers to access this.

<Create
  updateState={this.onSubmitUpdateState.bind(this)} // pass a binded handler
/>

Upvotes: 2

Sham Gir
Sham Gir

Reputation: 324

Use Arrow function or bind your action, if you are using this.state in any of method.

Arrow Function :

  onSubmitUpdateState = (newNote) => {
    const oldState = [...this.state.notes]
    // Problem 2)
    this.setState({notes: [...oldState, newNote]});
  }

bind method : Create constructor and add this line in your constructor

 this.onSubmitUpdateState = this.onSubmitUpdateState.bind(this)

Without constructor

<Create updateState={this.onSubmitUpdateState.bind(this)}/> 

Upvotes: 3

Nadir Laskar
Nadir Laskar

Reputation: 4150

The error is complaining that this.state is undefined, you may need to pass the function this.onSubmitUpdateState as arrow function

I am not sure though why is this not bounded when function passed as props

<Create updateState={()=> this.onSubmitUpdateState()}/>

there is another answer that explains why is it happening so.

Upvotes: 1

Atin Singh
Atin Singh

Reputation: 3690

Try doing this

class App extends Component{
  state = {
    notes: []
  }

  componentDidMount(){
    fetch('http://localhost:3001/notes')
      .then(resp => resp.json())
      .then(data => this.setState({notes: data}))
  }



  onSubmitUpdateState = (newNote) => {   //change to arrow function//
    const oldState = [...this.state.notes]
    // Problem 2)
    this.setState({notes: [...oldState, newNote]});
  }
  render(){
    return(
      <div className="App">
        <Notes notes={this.state.notes}/>
        <Create updateState={this.onSubmitUpdateState}/>
      </div>
    )
  }
}

export default App;

The arrow syntax takes care of the this binding for you. Or you can bind the function inside constructor. `

Basically arrow functions preserve this context when they are called

Upvotes: 1

Related Questions