Avec l'avènement useReducer
et la useContext
gestion de l'état des applications, il est devenu beaucoup plus pratique et le besoin d'utiliser Redux a disparu.
Lorsque j'ai abandonné Redux pour la première fois au profit du standard, useReducer
j'ai ressenti un manque de fonctionnalités utiles:
- useSelector . Vous permet d'optimiser le rendu des composants utilisés
useContext
avecmemo
. - La seule expédition . Simplifie la mise à jour de l'état de l'application car vous n'avez pas besoin d'utiliser une distribution distincte pour chacune
useReducer
. - Cache . Vous n'avez pas à vous soucier de la mise en cache de tout le monde
useReducer
.
Puis j'ai décidé d'essayer d'améliorer le standard useReducer
en ajoutant ces 3 fonctions. Cette idée a évolué vers une nouvelle petite bibliothèque que j'appelle Flex Reducer .
Fait intéressant!
Flex Reducer n'est utilisé dans useReducer
aucune useContext
de ses implémentations.
Voyons les avantages et les inconvénients de l'utilisation du standard useReducer
+ useContext
et du Flex Reducer en utilisant une application Todo typique à titre d'exemple.
Tout d'abord, créons un fichier principal dans lequel l'arborescence React est rendue.
// index.js
import TodoApp from "./TodoApp";
const rootElement = document.getElementById("root");
ReactDOM.render(
<TodoApp />,
rootElement
);
Remarque : Plus besoin de combiner les réducteurs, de créer un magasin et un fournisseur. Hourra! :)
Maintenant, écrivons le composant principal de l'application Todo en utilisant useReducer
.
// TodoApp.js
import { useReducer, createContext, useMemo } from 'react';
import AddTodo from './AddTodo';
import TodoList from './TodoList';
export const AppContext = createContext(null);
const cache = {};
export default function TodoApp() {
const [state, dispatch] = useReducer(reducer, cache.state || initialState);
cache.state = state;
const actions = useMemo(() => ({
setInput: (value) => {
dispatch({
type: 'SET_INPUT',
payload: value
})
},
addTodo: ({ id, content }) => {
dispatch({
type: 'ADD_TODO',
payload: { id, content }
})
}
}), []);
return (
<AppContext.Provider value=[state, actions]>
<div className="todo-app">
<h1>{state.title}</h1>
<input value={state.input} onChange={e => actions.setInput(e.target.value)} />
<AddTodo />
<TodoList />
</div>
</AppContext>
);
}
Cela semble bon. Maintenant la même chose mais en utilisant Flex Reducer.
// TodoApp.js
import { useFlexReducer, dispatch } from 'flex-reducer';
import AddTodo from './AddTodo';
import TodoList from './TodoList';
export const setInput = (value) => dispatch({
type: SET_INPUT,
payload: value
});
export const addTodo = ({ id, content }) => dispatch({
type: ADD_TODO,
payload: { id, content }
});
export default function TodoApp() {
const [state] = useFlexReducer('app', reducer, initialState);
return (
<div className="todo-app">
<h1>{state.title}</h1>
<input value={state.input} onChange={e => setInput(e.target.value)} />
<AddTodo />
<TodoList />
</div>
);
}
, .
:
- React Context.
- .
- actions
dispatch
.
re-renders Add Todo button.
.
// AddTodo.js
import { useContext, memo } from 'react';
import { appContext } from './TodoApp';
const genId = () => Math.rand();
const AddTodo = memo(({ input, actions }) => {
function handleAddTodo() {
if (content) {
actions.addTodo({ id: genId(), content: input });
actions.setInput('');
}
}
return (
<button onClick={handleAddTodo}>
Add Todo
</button>
);
})
export default const MemoizedAddTodo = () => {
const [state, actions] = useContext(appContext);
return (
<AddTodo input={state.input} actions={actions} />
);
}
useContext
AddTodo render memo
. -, useContext
props.
Flex Reducer.
// AddTodo.js
import { useSelector } from 'flex-reducer';
import { addTodo, setInput } from "./TodoApp";
const genId = () => Math.rand();
export default const AddTodo = React.memo(() => {
const content = useSelector(state => state.app.input);
function handleAddTodo() {
if (content) {
addTodo({ id: genId(), content });
setInput('');
}
}
return (
<button onClick={handleAddTodo}>
Add Todo
</button>
);
})
. -. useSelector
, re-render input
.
, , Flex Reducer .
remote data, , react-query.
useReducer.
// TodoApp.js
import { useReducer, createContext, useMemo } from 'react';
import { useQuery } from 'react-query';
import AddTodo from './AddTodo';
import TodoList from './TodoList';
export const AppContext = createContext(null);
const cache = {};
export default function TodoApp() {
const [reducerState, dispatch] = useReducer(reducer, cache.state || initialState);
cache.state = reducerState;
const actions = useMemo(() => ({
setInput: (value) => {
dispatch({
type: 'SET_INPUT',
payload: value
})
},
addTodo: ({ id, content }) => {
dispatch({
type: 'ADD_TODO',
payload: { id, content }
})
}
}), []);
const todos = useQuery('todos', fetchTodoList);
const state = { ...reducerState, todos };
return (
<AppContext.Provider value=[state, actions]>
<div className="todo-app">
<h1>{state.title}</h1>
<input value={state.input} onChange={e => actions.setInput(e.target.value)} />
<AddTodo />
<TodoList />
</div>
</AppContext>
);
}
. .
Flex Reducer.
// TodoApp.js
import { useFlexReducer, dispatch } from 'flex-reducer';
import { useQuery } from 'react-query';
import AddTodo from './AddTodo';
import TodoList from './TodoList';
export const setInput = (value) => dispatch({
type: SET_INPUT,
payload: value
});
export const addTodo = ({ id, content }) => dispatch({
type: ADD_TODO,
payload: { id, content }
});
export const setTodos = (todos) => dispatch({
type: SET_TODOS,
payload: todos
});
export default function TodoApp() {
const [state] = useFlexReducer('app', reducer, initialState);
const todos = useQuery('todos', fetchTodoList);
React.useEffect(() => {
setTodos(todos);
}, [todos]);
return (
<div className="todo-app">
<h1>{state.title}</h1>
<input value={state.input} onChange={e => setInput(e.target.value)} />
<AddTodo />
<TodoList />
</div>
);
}
todos query.
Utiliser useReducer
+ useContext
pour gérer l'état de l'application est assez pratique. Mais cela nécessite un travail «manuel» soigneux avec le contexte et le cache.
Flex Reducer s'occupe de cela, améliore la lisibilité du code, facilite l'écriture des optimisations memo
et réduit la durée. Mais des problèmes apparaissent lors de l'utilisation déclarative de données distantes (comme avec react-query).
Attention!
Flex Reducer n'est qu'une expérience et n'a pas été utilisé en production.
Merci d'avoir lu. Toutes les pensées sont appréciées.
Un exemple fonctionnel de l'application Todo peut être trouvé ici .
Lien vers le référentiel Flex Reducer