I'm having an issue with useReducer + Typescript + async. I just can't do it! When I call anything from async function it return a Promise which break my code. When I tried to get it other way, the component is doesn't re-render! That is Driving me crazy.
I wrote this issue on my personal project which represents the problem I have! https://github.com/igormcsouza/full-stack-todo/issues/15
What I can do to make it work?
I want to make a call from the backend populate the list with the information I got from backend. So my frontend need to re-render every time any change is done to the backend (when add, update or delete any registry there).
reducers.tsx
import { delete_todo, fetch_todos, insert_todo, update_todo } from
"../utils";
import { State, Actions, Todo } from "../TodoContext";
export const INITIAL_STATE: State = {
todos: [],
};
export const reducer = (state: State, action: Actions): State => {
let newState: State = {};
switch (action.type) {
case "POPULATE":
fetch_todos().then((value) => (newState = value));
return newState;
case "ADD_TODO":
if (state.todos) {
const newTodo: Todo = {
when: (+new Date()).toString(),
task: action.payload,
checked: false,
by: "Igor Souza",
};
insert_todo(newTodo);
}
fetch_todos().then((value) => (newState = value));
return newState;
case "CHECK_TODO":
action.payload.checked = !action.payload.checked;
update_todo(action.payload);
fetch_todos().then((value) => (newState = value));
return newState;
case "EDIT_TODO":
let todo = action.payload.task;
todo.task = action.payload.newTaskName;
update_todo(todo);
fetch_todos().then((value) => (newState = value));
return newState;
case "DELETE_TODO":
delete_todo(action.payload);
fetch_todos().then((value) => (newState = value));
return newState;
default:
return state;
}
};
utils.tsx (with the axios calls)
import axios from "axios";
import { State, Todo } from "./TodoContext";
// const base = "http://backend:2500";
const base = "https://full-stack-todo-bknd.herokuapp.com";
export async function fetch_todos(): Promise<State> {
let todos: State = {};
await axios
.get<State>(base + "/api/todo")
.then((response) => {
const { data } = response;
todos = data;
})
.catch((e) => console.log(e));
console.log(typeof todos.todos);
return todos;
}
export async function insert_todo(todo: Todo) {
await axios.post(base + "/api/todo", todo).catch((e) => console.log(e));
}
export async function update_todo(todo: Todo) {
await axios.put(base + "/api/todo/" + todo.id).catch((e) => console.log(e));
}
export async function delete_todo(todo: Todo) {
await axios
.delete(base + "/api/todo/" + todo.id)
.catch((e) => console.log(e));
}
context.tsx (Context APi)
import React, { createContext, useReducer } from "react";
import { reducer, INITIAL_STATE } from "./reducers";
type ContextProps = {
state: State;
dispatch: (actions: Actions) => void;
};
export interface Todo {
id?: string;
task: string;
when: string;
checked: boolean;
by: string;
}
export interface State {
todos?: Array<Todo>;
}
export interface Actions {
type: string;
payload?: any;
}
export const TodoContext = createContext<Partial<ContextProps>>({});
const TodoContextProvider: React.FC = ({ children }) => {
const [state, dispatch] = useReducer(reducer, INITIAL_STATE);
return (
<TodoContext.Provider value={{ state, dispatch }}>
{children}
</TodoContext.Provider>
);
};
export default TodoContextProvider;