简体   繁体   中英

Updating a page at refresh AND change of state

I'm trying to build a todo page where I can input todos in my input field. All todos will be rendered below. I managed to build a form where I can type in a todo title and send it to my database. A small problem I'm having here is that I need to refresh the page after pushing the add button to see the new list. I assume this is because I use componentDidMount and this updates only at page refresh. Any idea how I can do this at page refresh (componentDidUpdate) AND at state change?

FRONT-END

import React from 'react'
import './Todo.css'  
import Todoitem from '../components/Todoitem'
import axios from 'axios'
import qs from "qs"
import DefaultLayout from "../layout/Default"

class Todo extends React.Component {
    constructor() {
        super()

        this.state = {
            title:"",
            todos:[]
        }
        this.handleChange=this.handleChange.bind(this)
        this.handleSubmit=this.handleSubmit.bind(this)
    }

    componentDidMount(){
        axios({
            method: "GET",
            url: `${process.env.REACT_APP_API_BASE}/todo`,
            withCredentials: true
        })
            .then(response => {
            console.log(response)
            let todolist = response.data;
            this.setState({todos:todolist})
        })
        .catch(error => {
            console.log("You've made an error when getting the todos charles: ",error)
        })
    }

    handleChange(event){
        event.preventDefault()
        let name = event.target.name
        let value = event.target.value
        this.setState({
            [name]:value
        })
        console.log(this.state.title)
    }

    handleSubmit(event){
        event.preventDefault()
        if (!this.state.title) {
            debugger
        }  
        axios({
            method: "POST",
            url: `${process.env.REACT_APP_API_BASE}/todo`,
            data: qs.stringify({title: this.state.title}),
            headers: {"content-type": "application/x-www-form-urlencoded"},
            withCredentials: true
        })
        .then((response) => {
            console.log(response)
        })
        .catch((error) => {
            console.log(error.response)
        })
    }

    handleDelete(todoId){
        axios
        .delete(`${process.env.REACT_APP_API_BASE}/todo/${todoId}`)
        .then(response => {
            const remainingTodos = this.state.todos.filter(element => element._id !== todoId)
            this.setState({
                todos: remainingTodos
            })
        })
        .catch(err => console.log(err))
    }

    render() {
        return (
            <div>
                <DefaultLayout>
                <h1>To-do things for this app</h1>
                <h2 className="todotitle">Add your to-do here, Charles!</h2>
                <form className="todocontainer" onClick={this.handleSubmit}> 
                    <div className="inputbuttonandfield">    
                        <div className="inputcontainer">
                            <div className="captionpart">
                                <label className="captionlabel" htmlFor="title">Add to-do:</label><br></br>
                                <input className="captionform" type="text" name="title" value={this.state.title} placeholder="Type your to-do here!" onChange={(e) => this.handleChange(e)}></input>
                                <button className="shootbutton">Add!</button>
                            </div>
                        </div> 
                    </div>
                </form> 

                {
                    this.state.todos.map(element=> (
                     <div className="todosoverviewlister" key={element._id}>
                        <Todoitem id={element._id} title={element.title} />

                        <button className="tododelete" onClick={()=> this.handleDelete(element._id)}>Delete</button>
                     </div>
                    ))
                }

                </DefaultLayout>
            </div>
        )
    }
}

export default Todo

Todomodel

const mongoose = require("mongoose")
const Schema = mongoose.Schema

const todoSchema = new Schema({
    title: String
})

const Todo = mongoose.model("todos",todoSchema)

module.exports = Todo

BACKEND

//request todos
router.get("/todo", (req,res) => {
  Todo
  .find()
  .then(response => {
    res.json(response)
  })
  .catch(error => {
    res.json(error)
  })
})

//delete todo
router.delete("/todo/:id", (req,res)=>{
  Todo
  .findByIdAndDelete(req.params.id)
  .then(response => {
    res.json(response)
  })
  .catch(error => {
    res.json(error)
  })
})

I believe the issue is that you're not updating the state when you submit (during the add operation). In your delete, you correctly keep the list in state synced with the list on the server by removing the element locally as well. In the add, you should to something similar by adding the new element to the list in state (or more precisely, make a deep copy and overwrite the one in state). That should do it.

There is no need to refetch the entire list from the server unless there are multiple users operating on the same list. If that's the case, you can add a get() call in the response of your submit. As long as the response of that operation writes to state, it will update correctly. But again, avoid that unless you need it as it will make your app slower and less responsive.

You can either update the state or sync up with database by sending another GET. Let me break it down into 2 solutions:

  • Just update the state
  • Make a GET request after the POST request and update the state

Just update the state

// you code ...
handleSubmit(event){
  event.preventDefault()
  const newTodo = { title: this.state.title };                // extract your todo into const

  axios({
      method: "POST",
      url: `${process.env.REACT_APP_API_BASE}/todo`,
      data: qs.stringify(newTodo),                         // send todo in the POST
      headers: {"content-type": "application/x-www-form-urlencoded"},
      withCredentials: true
  })
  .then((response) => {
      console.log(response)
      this.setState(prevState => ({                        // immutably update the state
        todos: [...prevState.todos, newTodo]
      }));
  })
  .catch((error) => {
      console.log(error.response)
  })
}
// your code ...

Send GET after POST:

// your Todo component
class Todo extends React.Component {
  constructor() {
    super();

    this.state = {
      title: "",
      todos: [],
    };
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  // extract method for loading TODOs (your previous componentDidMount)
  loadTodos = () => {
    axios({
      method: "GET",
      url: `${process.env.REACT_APP_API_BASE}/todo`,
      withCredentials: true,
    })
      .then((response) => {
        console.log(response);
        let todolist = response.data;
        this.setState({ todos: todolist });
      })
      .catch((error) => {
        console.log(
          "You've made an error when getting the todos charles: ",
          error
        );
      });
  }

  componentDidMount() {
    this.loadTodos();                                 // use the extracted method
  }

  handleChange(event) {
    event.preventDefault();
    let name = event.target.name;
    let value = event.target.value;
    this.setState({
      [name]: value,
    });
    console.log(this.state.title);
  }

  handleSubmit(event) {
    event.preventDefault();
    if (!this.state.title) {
      debugger;
    }
    axios({
      method: "POST",
      url: `${process.env.REACT_APP_API_BASE}/todo`,
      data: qs.stringify({ title: this.state.title }),
      headers: { "content-type": "application/x-www-form-urlencoded" },
      withCredentials: true,
    })
      .then((response) => {
        console.log(response);
        this.loadTodos();                            // use the extracted method
      })
      .catch((error) => {
        console.log(error.response);
      });
  }

  handleDelete(todoId) {
    axios
      .delete(`${process.env.REACT_APP_API_BASE}/todo/${todoId}`)
      .then((response) => {
        const remainingTodos = this.state.todos.filter(
          (element) => element._id !== todoId
        );
        this.setState({
          todos: remainingTodos,
        });
      })
      .catch((err) => console.log(err));
  }

  render() {
    return (
      <div>
        <DefaultLayout>
          <h1>To-do things for this app</h1>
          <h2 className="todotitle">Add your to-do here, Charles!</h2>
          <form className="todocontainer" onClick={this.handleSubmit}>
            <div className="inputbuttonandfield">
              <div className="inputcontainer">
                <div className="captionpart">
                  <label className="captionlabel" htmlFor="title">
                    Add to-do:
                  </label>
                  <br></br>
                  <input
                    className="captionform"
                    type="text"
                    name="title"
                    value={this.state.title}
                    placeholder="Type your to-do here!"
                    onChange={(e) => this.handleChange(e)}
                  ></input>
                  <button className="shootbutton">Add!</button>
                </div>
              </div>
            </div>
          </form>

          {this.state.todos.map((element) => (
            <div className="todosoverviewlister" key={element._id}>
              <Todoitem id={element._id} title={element.title} />

              <button
                className="tododelete"
                onClick={() => this.handleDelete(element._id)}
              >
                Delete
              </button>
            </div>
          ))}
        </DefaultLayout>
      </div>
    );
  }
}

export default Todo;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM