Home > database >  React rerender component on database change
React rerender component on database change

Time:12-28

Im trying to develop a dynamic todoList.

I have two components TodoList, and Todo. TodoList fetches with useSWR the data from Mongo and puts the data in a div depending if they're completed or not.

Todo component has a button to mark if the task its completed or not. When you click the input check it changes automatically on mongo.

Now I want that these change is reflected in the list too. For example, if a task its not completed it appears in todos. enter image description here

When I check the input in DB its marked as completed, but It still appears as not completed: enter image description here

TodoList.tsx

import React, { useState } from 'react';
import useSWR from 'swr';
import axios from 'axios';
import Todo from './Todo';

const fetcher = (url: string) => axios.get(url).then(res => res.data)

export default function TodoList() {

  const { data, error } = useSWR('/api/v1/todos-all', fetcher)
  if (error) return <div>failed to load</div>;
  
  if (!data) return <div>loading...</div>;


  return (
    <div>
      <div>
        <h1>To Do</h1>
        {
          data.todos.map((todo: any) => !todo.completed ? (<Todo key={todo._id} id={todo._id} title={todo.title} completed={todo.completed}/>) : (<></>))
        }
      </div>

      <div>
        <h1>Completed</h1>
        {
          data.todos.map((todo: any) => todo.completed ? (<Todo key={todo._id} id={todo._id} title={todo.title} completed={todo.completed}/>) : (<></>))
        }
      </div>
      
    </div>
  )
}

Todo.tsx

import React, { useState } from 'react';
import styled from 'styled-components';
import axios from 'axios';

interface ITodo {
    title: string;
    id: string;
    completed: boolean;
}

const TodoSyle = styled.div`
    background-color: #fff;
    padding: 20px;
    margin: 10px;
    border: 1px solid #eeeeee;
    border-radius: 10px;
    `;

export default function Todo({title, id, completed}: ITodo) {

    const [checked, setChecked]= useState(completed)

    const handleClick = () => {

        setChecked(!checked)

        axios.put(`/api/v1/todo/${id}`, {"completed": `${!completed}`})
    }

    console.log(id)

    return (
        <TodoSyle>
            {title}
            <input type="checkbox" checked={checked} onChange={handleClick}/>
        </TodoSyle>
    )
}

Until you refresh the pag., which fetches the data again from db and it works. Is there a way to make rerender the app when the check button is clicked? Or there is a beeter approach to these problem?

Thanks

CodePudding user response:

useSWR won't automatically refetch (revalidate) data by default, the cache will still be having the old data.

You can either enable automatic refetch using the refreshInterval option.

const { data, error } = useSWR('/api/v1/todos-all', fetcher, { refreshInterval: 1000 });

Or explicitly update the data in cache yourself using a mutation after the PUT request to the API.

axios.put(`/api/v1/todo/${id}`, {"completed": `${!completed}`})
  .then(() => {
      mutate()
  })

CodePudding user response:

I have never used useSWR hook before, but maybe it could help you.

You should move handleClick function to TodoList.tsx and use mutate to update client cache (data)

const { data, error, mutate } = useSWR('/api/v1/todos-all', fetcher);

...

const handleClick = (todo, value) => {
  axios.put(`/api/v1/todo/${todo._id}`, { completed: !value}).then(() => {
    todo.completed = value;
    mutate([...todos]);
  });
};

return
  ...
    {
      data.todos.map((todo: any) =>
        todo.completed ? (
          <Todo key={todo._id} todo={todo} onClick={handleClick} />
        ) : null
      );
    }

Todo.tsx

...
<input
  type="checkbox"
  value={todo.checked}
  onChange={(e) => onClick(todo, e.target.checked)}
/>
  • Related