stk1234
stk1234

Reputation: 1136

Force Component Rerender in React

I have a react app that fetches a list of todos from the backend, displays them in a list, and offers you a button to add a new one.

I notice that the newly added to do will only appear in the list AFTER I refresh the page (e.g. after it fetches from the backend). I'd like to force the component to re-render with the newly added to do, ideally without a backend call. How can I do that?

Component AFTER I've hit "add" - text stays in the box, does not appear in the list enter image description here

Page AFTER I've hit add and refreshed the page - appears below - I'd like this behavior to happen without a manual refresh enter image description here

App.tsx

    import React, {useState, useEffect} from "react"
import './App.css';
import APIHelper from "./APIHelper";

function App() {
  const [todos, setTodos] = useState([])
  const [todo, setTodo] = useState("")

  useEffect(() => {
    const fetchTodoAndSetTodos = async () => {
      const todos = await APIHelper.getAllTodos()
      setTodos(todos)
    }
    fetchTodoAndSetTodos()
  }, [])

  const createTodo = async (e: { preventDefault: () => void; }) => {
    e.preventDefault()
    const newTodo = await APIHelper.createTodo(todo)
    // @ts-ignore
    setTodos([...todos, newTodo])
  }

  return (
          <div className="App">
            <div>
              <input
                      id="todo-input"
                      type="text"
                      value={todo}
                      onChange={({target}) => setTodo(target.value)}
              />
              <button type="button" onClick={createTodo}>
                Add
              </button>
            </div>
            <ul>
              {todos.map(({_id, task, completed}, i) => (
                      <li
                              key={i}
                              className={completed ? "completed" : ""}
                      > {task}
                      </li>
              ))}
            </ul>
          </div>
  )
}

export default App

APIHelper.js

import axios from "axios"

const API_URL_GET = "http://localhost:8080/"
const API_URL_CREATE = "http://localhost:8080/create"

async function createTodo(task) {
    const { data: newTodo } = await axios.post(API_URL_CREATE, {
        task,
    })
    return newTodo
}

async function getAllTodos() {
    const { data: todos } = await axios.get(API_URL_GET)
    return todos
}
export default { createTodo, getAllTodos }



  [1]: https://i.sstatic.net/CuYUK.png
  [2]: https://i.sstatic.net/PDne4.png

Upvotes: 0

Views: 188

Answers (3)

Peter
Peter

Reputation: 1259

For me it seems like that APIHelper.createTodoreturns a wrong object. The list item is at the screen, but has no taskproperty.
As mentioned above, Try to prevent forceUpdate() the component.

async function createTodo(task) {
    const { data: newTodo } = await axios.post(API_URL_CREATE, {
        task,
    });
    // check this newTodo value
    return newTodo
}

To reset the inputfield, setTodo("")to an empty string

Upvotes: 0

Wahab Shah
Wahab Shah

Reputation: 2276

Normally you should avoid this but still here you go you can using forceUpdate

In your component, you can call this.forceUpdate() to force a rerender.

Documentation: https://facebook.github.io/react/docs/component-api.html

Upvotes: 0

ChrisR
ChrisR

Reputation: 4008

Inside the createTodo function, you'll need to reset your todo state. Which means setting it to an empty string again:

const createTodo = async (e: { preventDefault: () => void; }) => {
    // ...
    setTodo(''); // Here
}

Upvotes: 1

Related Questions