Templee_0302) TodoLists Toggle Todo not sorted properly ( useCallback & state)


SQ



As I render, The TodoLists are sorted by 2 standards
1) time left
2) is done or not
todos that is 'not' done comes first
and after todos being divided into 'done', 'not done'
then, they are sorted by 'time left'

Purpose


If I click one of them, and change 'done state' ,
I want to re-order todos above

Code for toggling

  const toggleTodo: ToggleTodo = useCallback(
    (SelectedTodoId: Todo) => {
      let didChange = false;
      const newTodos = todos.map((todo) => {
        if (todo == SelectedTodoId) {
          console.log('toggleTodo');
          didChange = true;
          return {
            ...todo,
            is_finished: !todo.is_finished,
          };
        }
        return todo;
      });
      setTodos(newTodos);
    },
    [setTodos]
  );

Code for useEffect ( sort todos in 1st render )

  useEffect(() => {
    // 1. is_finished : false
    // 2. 만들어진 시간 기준 sorting
    setTodos(
      todos.slice().sort((a, b) => {
        return (
          // is_finished false 가 먼저
          Number(a.is_finished) - Number(b.is_finished) ||
          // 가장 최근 data 가 위로
          new Date(a.due).valueOf() - new Date(b.due).valueOf()
        );
      })
    );
    console.log('hello useEffect Loading again');
  }, []);

BUT


It works in strange way

as you can see, todos above are sorted wrongly

Solution


Usecallback dependency in "toggleTodo"
[setTodos]
we set 'setTodos' at dependency array,

❤ useCallback


If you use useCallback to certain function,
it means that result of function will return memoized version of result that only changes when the 'values' in 'dependency array' changes
that is,
even if 'todos' state changes after toggle,
it will still return memoized , previous version of 'todos', unless you put 'todos' state in 'dependency array'
So, when you use 'usecallback' to certain function which includes 'state', you must put 'state' into dependency array