Mit dem Aufkommen useReducerund der useContextVerwaltung des App-Status ist es viel bequemer geworden, und die Notwendigkeit, Redux zu verwenden, ist verschwunden.
Als ich Redux zum ersten Mal zugunsten der Standardversion fallen ließ, useReducerfehlten mir einige nützliche Funktionen:
- useSelector . Ermöglicht die Optimierung des Renderings von Komponenten, die
useContextmit verwendet werdenmemo. - Der einzige Versand . Vereinfacht das Aktualisieren des App-Status, da Sie nicht für jeden einen separaten Versand verwenden müssen
useReducer. - Cache . Sie müssen sich nicht darum kümmern, alle zwischenzuspeichern
useReducer.
Dann habe ich beschlossen, den Standard useReducerdurch Hinzufügen dieser 3 Funktionen zu verbessern . Diese Idee hat sich zu einer neuen kleinen Bibliothek entwickelt, die ich Flex Reducer nenne .
Interessante Tatsache!
Flex Reducer wird in useReducerkeiner useContextseiner Implementierungen verwendet.
Lassen Sie uns die Vor- und Nachteile der Verwendung von Standard useReducer+ useContextund Flex Reducer anhand einer typischen Todo-App als Beispiel sehen.
Zuerst erstellen wir eine Hauptdatei, in der der Reaktionsbaum gerendert wird.
// index.js
import TodoApp from "./TodoApp";
const rootElement = document.getElementById("root");
ReactDOM.render(
<TodoApp />,
rootElement
);
Hinweis : Keine Reduzierungen mehr kombinieren, Store und Provider erstellen. Hurra! :) :)
Schreiben wir nun die Hauptkomponente der Todo App mit 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>
);
}
Sieht gut aus. Jetzt das gleiche, aber mit 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.
Die Verwendung von useReducer+ useContextzum Verwalten des App-Status ist sehr praktisch. Dies erfordert jedoch sorgfältige "manuelle" Arbeit mit Kontext und Cache.
Flex Reducer kümmert sich darum, verbessert die Lesbarkeit des Codes, erleichtert das Schreiben von Optimierungen memound reduziert die Zeitspanne. Bei der deklarativen Arbeit mit Remote-Daten treten jedoch Probleme auf (wie bei der React-Query).
Beachtung!
Flex Reducer ist nur ein Experiment und wurde in der Produktion nicht verwendet.
Danke fürs Lesen. Alle Gedanken werden geschätzt.
Ein funktionierendes Beispiel für die Todo-App finden Sie hier .
Link zum Flex Reducer-Repository