Skip to content

Commit

Permalink
Fix edditing pushing to the api at every stroke
Browse files Browse the repository at this point in the history
  • Loading branch information
garronej committed May 29, 2024
1 parent 557ed86 commit e0bc617
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 10 deletions.
34 changes: 28 additions & 6 deletions src/components/TodoApp/Todo.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { memo, useState } from "react";
import { memo, useState, useEffect, useReducer } from "react";
import { tss } from "tss-react";
import { fr } from "@codegouvfr/react-dsfr";
import { Button } from "@codegouvfr/react-dsfr/Button";
import Checkbox from "@mui/material/Checkbox";
import { useConstCallback } from "tools/useConstCallback";

export type Todo = {
id: string;
Expand All @@ -19,12 +20,33 @@ type TodoProps = {
};

export const Todo = memo((props: TodoProps) => {
const { className, todo, onUpdateTodoText, onToggleTodo, onDeleteTodo } = props;
const { className, todo, onToggleTodo, onDeleteTodo } = props;

const [isEditing, setIsEditing] = useState(false);
// NOTE: Make sure it's not stale for when used in the reducer.
// We know it's constant because we also used useListCallbacks() in the parent component
// but this component is not supposed to be aware of that.
const onUpdateTodoText = useConstCallback(props.onUpdateTodoText);

const [isEditing, setIsEditing] = useReducer((isEditing: boolean, isEditing_new: boolean) => {
if (isEditing_new === isEditing) {
return isEditing;
}

if (!isEditing_new) {
onUpdateTodoText(text);
}

return isEditing_new;
}, false);

const { classes, cx } = useStyles({ isEditing });

const [text, setText] = useState(todo.text);

useEffect(() => {
setText(todo.text);
}, [todo.text]);

return (
<div className={cx(classes.root, className)}>
<Checkbox checked={todo.isDone} onChange={() => onToggleTodo()} />
Expand All @@ -33,8 +55,8 @@ export const Todo = memo((props: TodoProps) => {
{isEditing ? (
<input
className={cx(fr.cx("fr-input"), classes.input)}
value={todo.text}
onChange={e => onUpdateTodoText(e.target.value)}
value={text}
onChange={e => setText(e.target.value)}
onBlur={() => setIsEditing(false)}
/>
) : (
Expand All @@ -44,7 +66,7 @@ export const Todo = memo((props: TodoProps) => {

<div className={classes.buttonsWrapper}>
<Button
iconId="ri-pencil-line"
iconId={isEditing ? "ri-check-line" : "ri-pencil-line"}
onClick={() => setIsEditing(!isEditing)}
priority="secondary"
title="Edit"
Expand Down
7 changes: 3 additions & 4 deletions src/components/TodoApp/TodoApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ export function TodoApp(props: Props) {

const { classes, cx } = useStyles();

const getOnUpdateTodoText = useListCallbacks(([todoId]: [string], [text]: [string]) =>
onUpdateTodoText(todoId, text)
);

/*
If we use this custom hook instead of just doing:
onToggleTodo={()=> onToggleTodo(todo.id)}
Expand All @@ -34,6 +30,9 @@ export function TodoApp(props: Props) {
Note: This is the state of the art for React 18. React 19 shuffles the deck with it's pre-compiler.
*/
const getOnUpdateTodoText = useListCallbacks(([todoId]: [string], [text]: [string]) =>
onUpdateTodoText(todoId, text)
);
const getOnToggleTodo = useListCallbacks(([todoId]: [string]) => onToggleTodo(todoId));
const getOnDeleteTodo = useListCallbacks(([todoId]: [string]) => onDeleteTodo(todoId));

Expand Down
17 changes: 17 additions & 0 deletions src/tools/useConstCallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useRef, useState } from "react";
import { Parameters } from "tsafe/Parameters";

/** https://stackoverflow.com/questions/65890278/why-cant-usecallback-always-return-the-same-ref */
export function useConstCallback<T extends ((...args: any[]) => unknown) | undefined | null>(
callback: NonNullable<T>
): T {
const callbackRef = useRef<typeof callback>(null as any);

callbackRef.current = callback;

return useState(
() =>
(...args: Parameters<T>) =>
callbackRef.current(...args)
)[0] as T;
}

0 comments on commit e0bc617

Please sign in to comment.