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)}
/>