Skip to content

Commit

Permalink
perf: separate state and state setters in TodoContext
Browse files Browse the repository at this point in the history
  • Loading branch information
przwojwwp committed Oct 10, 2024
1 parent a6c98b9 commit 630ebde
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 25 deletions.
8 changes: 6 additions & 2 deletions src/components/Footer/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { useMemo } from 'react';
import { Filter } from '../../types/Filter';
import { useTodoContext } from '../context/TodoContext';
import {
useTodoContextState,
useTodoContextUpdater,
} from '../context/TodoContext';

const FILTERS: Record<Filter, string> = {
All: '#/',
Expand All @@ -9,7 +12,8 @@ const FILTERS: Record<Filter, string> = {
};

export const Footer = () => {
const { todos, filter, setFilter, deleteMultipleTodos } = useTodoContext();
const { todos, filter } = useTodoContextState();
const { setFilter, deleteMultipleTodos } = useTodoContextUpdater();

const incompleteTodosCount = useMemo(() => {
return todos.filter(todo => !todo.completed).length;
Expand Down
9 changes: 6 additions & 3 deletions src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { useState } from 'react';
import { useTodoContext } from '../context/TodoContext';
import {
useTodoContextState,
useTodoContextUpdater,
} from '../context/TodoContext';

export const Header = () => {
const [todoTitle, setTodoTitle] = useState('');
const { addTodo, headerInputRef, todos, toggleMultipleTodosStatus } =
useTodoContext();
const { headerInputRef, todos } = useTodoContextState();
const { addTodo, toggleMultipleTodosStatus } = useTodoContextUpdater();

const handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
Expand Down
5 changes: 3 additions & 2 deletions src/components/TodoItem/TodoItem.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { Todo } from '../../types/Todo';
import { useTodoContext } from '../context/TodoContext';
import { useTodoContextUpdater } from '../context/TodoContext';

type Props = {
todo: Todo;
Expand All @@ -12,7 +12,8 @@ export const TodoItem = ({ todo: { id, title, completed } }: Props) => {
const renameInputRef = useRef<HTMLInputElement | null>(null);
const checkboxRef = useRef<HTMLInputElement | null>(null);

const { toggleTodoStatus, updateTodoTitle, deleteTodo } = useTodoContext();
const { toggleTodoStatus, updateTodoTitle, deleteTodo } =
useTodoContextUpdater();

const handleSubmit = useCallback(
(event: React.FormEvent) => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/Todos/Todos.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useTodoContext } from '../context/TodoContext';
import { useTodoContextState } from '../context/TodoContext';
import { TodoItem } from '../TodoItem/TodoItem';

export const Todos = () => {
const { filteredTodos } = useTodoContext();
const { filteredTodos } = useTodoContextState();

return (
<section className="todoapp__main" data-cy="TodoList">
Expand Down
60 changes: 44 additions & 16 deletions src/components/context/TodoContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,46 @@ import {
import { Todo } from '../../types/Todo';
import { Filter } from '../../types/Filter';

type TodoContextProps = {
type TodoContextStateProps = {
todos: Todo[];
headerInputRef: React.RefObject<HTMLInputElement>;
filter: Filter;
filteredTodos: Todo[];
};

type TodoContextUpdaterProps = {
addTodo: (title: string) => void;
toggleTodoStatus: (id: number) => void;
toggleMultipleTodosStatus: () => void;
updateTodoTitle: (id: number, title: string) => void;
deleteTodo: (id: number) => void;
headerInputRef: React.RefObject<HTMLInputElement>;
filter: Filter;
setFilter: (filter: Filter) => void;
deleteMultipleTodos: () => void;
filteredTodos: Todo[];
};

const TodoContext = createContext<TodoContextProps | undefined>(undefined);
const TodoContextState = createContext<TodoContextStateProps | undefined>(
undefined,
);

const TodoContextUpdater = createContext<TodoContextUpdaterProps | undefined>(
undefined,
);

export const useTodoContextState = () => {
const context = useContext(TodoContextState);

if (!context) {
throw new Error('useTodoContextState must be used within a TodoProvider');
}

return context;
};

export const useTodoContext = () => {
const context = useContext(TodoContext);
export const useTodoContextUpdater = () => {
const context = useContext(TodoContextUpdater);

if (!context) {
throw new Error('useTodoContext must be used within a TodoProvider');
throw new Error('useTodoContextUpdater must be used within a TodoProvider');
}

return context;
Expand Down Expand Up @@ -116,32 +135,41 @@ export const TodoProvider = ({ children }: TodoProviderProps) => {
setTodos(prevTodos => prevTodos.filter(todo => !todo.completed));
}, []);

const value = useMemo(
const stateValue = useMemo(
() => ({
todos,
headerInputRef,
filter,
filteredTodos,
}),
[filter, filteredTodos, todos],
);

const updaterValue = useMemo(
() => ({
addTodo,
toggleTodoStatus,
toggleMultipleTodosStatus,
updateTodoTitle,
deleteTodo,
headerInputRef,
filter,
setFilter,
filteredTodos,
deleteMultipleTodos,
}),
[
addTodo,
deleteMultipleTodos,
deleteTodo,
filter,
filteredTodos,
todos,
toggleMultipleTodosStatus,
toggleTodoStatus,
updateTodoTitle,
],
);

return <TodoContext.Provider value={value}>{children}</TodoContext.Provider>;
return (
<TodoContextState.Provider value={stateValue}>
<TodoContextUpdater.Provider value={updaterValue}>
{children}
</TodoContextUpdater.Provider>
</TodoContextState.Provider>
);
};

0 comments on commit 630ebde

Please sign in to comment.