Open utterances-bot opened 2 years ago
I got a task at work to create a simple application with CRUD aka internal blog ( app inside the app ) and here we go - useReducer() exactly what was necessary!
I used the logic from another of your article, here is my code:
in the component
const [posts, dispatch] = useReducer(reducer, []);
const [newPost, setNewPost] = useState("");
my reducer
export default function reducer(state, action) {
switch (action.type) {
case "add":
return [...state, action.post];
case "delete":
return state.filter((post) => post !== action.post);
default:
throw new Error();
}
}
I think that I will recreate a new post as an object later with the "title" and "body" of the post.
Thank you for help in my learning!
@Ellinsa You're welcome!
This was concise and very clear. Thank you for another very helpful article.
Very helpful, thank you~
This was concise and very clear. Thank you for another very helpful article.
Glad you like it @sergioreynoso!
Very helpful, thank you~
You're welcome @Cary123!
Hi Dmitri!
I am controlling a popup from a wrapper component using useReducer()
and sending in the data that I want to display on the popup using its payload from the children of this wrapper.
And on this popup I usually have two buttons that need to do different things depending on the child that opened the popup. Can you please tell what would be the best way to: first, write the initial state of the reducere to accept a new function and second, what would be the best way to send this function in the payload.
As of now, my initial reducer looks something in the lines of:
const [userActions, dispatch] = useReducer(reducer, [
{
popup: false,
...
action: new Function()
},
]);
my first case to get the data looks something like:
case "get-data":
return {
...userActions,
...action.payload,
}
and from the child I'm setting the action and open the popup like:
props.dispatch({
type: "get-data",
payload: {
popup: true,
action: function() {
return someFn(e)
}
}
})
and once the popup is open, when pressing confirm, I am calling the previously set function in a case like:
case "do-the-action":
return {
...userActions,
popup: false,
action: userActions.action()
}
Does this make sense? It's working but also causing some troubles in some instances and I would really like to now a best approach. Thank you for taking the time to read this! Adrian
To modularise the main reducer function to use and expose different reducer functions, it will be like. It follows Adapter Pattern.
const mainReducer = (reducer) => {
return (prevState, action) => {
const nextState = reducer(prevState, action);
return nextState;
};
};
const todoReducer = mainReducer((prevState, action) => {
const {type, payload} = action;
switch(type) {
case 'ADD' : ...
case 'REMOVE' : ...
default: throw new Error('Unhandled action');
}
});
export {todoReducer}
Hope it makes sense too. Thanks Dmitri for being there, I love your blog posts, Keep it up.
The custom Hook
import React, {useReducer} from "react"
function reducer( state, action ) {
switch( action.type ) {
case 'toggle': return !state
default: throw new Error()
}
}
export function useMyState( initialState ){
const [ state, dispatch ] = useReducer( reducer, initialState )
return{ state, dispatch }
}
The React Component
import React from "react"
import { useMyState } from "../hooks/useMyState"
export function MyComponent(){
const { state: sayHello, dispatch: setSayHello } = useMyState( false )
return(
<div>
<p>{ sayHello ? "Hello" : "" }</p>
<button onClick={ () => setSayHello( { type: 'toggle'} ) }>Greeting</button>
</div>
)
}
@ChristBM Thanks for trying the challenge, however the requirement is useMyState
to implement the same API const [state, setState] = useMyState(initialState)
as useState()
.
In your case useMyState()
is just a toggler.
Hi Hi Dmitri!, Thanks for sharing your skills with us,
would you please tell me what this code does ?
return () => { clearInterval(idRef.current); idRef.current = 0; };
they are inside the useEffect at line 19 on CodeSandBox. I as thinking if this code runs when the action type is "stop", is it true ?
Thanks !
would you please tell me what this code does ?
return () => { clearInterval(idRef.current); idRef.current = 0; };
@zackniyokwizera The code that you mention is the cleanup function of useEffect()
hook.
This code will stop the watch if the component unmounts.
I understand now, Thanks !
Here is my solution to your challenge:
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'set':
return action.payload;
default:
throw new Error('invalid action.type');
}
}
export function useMyState(initialState) {
const [state, dispatch] = useReducer(reducer, initialState);
const setState = (payload) => dispatch({ type: 'set', payload: payload });
return [state, setState];
}
import React from 'react';
import { useMyState } from './hooks/useMyState';
export function MyComponent() {
const [state, setState] = useMyState(0);
return (
<div>
<p>{state}</p>
<button onClick={() => setState(state + 1)}>increment</button>
</div>
);
}
Great post! Especially the example "engine order telegraph" is very helpful to understand reducer.
@bluevoxInc's hook works for me! :)
Great post! Especially the example "engine order telegraph" is very helpful to understand reducer.
@bluevoxInc's hook works for me! :)
You're welcome @inimist!
I've a question that , what is the use case of the dispatch function return by the useReducer ?
I've a question that , what is the use case of the dispatch function return by the useReducer ?
The dispatch()
function dispatches actions.
Hi Dimitri,
Thank you so much for writing and providing these clear and detailed articles on React and JavaScript! I've been making a web app and back end in JavaScript and React and so often when I need to answer a question on how things work your website has the best answers. You saved me so much time and headache with your articles. Thank you again!
I'm probably late to the party, but thanks for a great article! Loved the final little challenge too. Seems to work, haha.
import { useReducer } from 'react';
export const useMyState = (initialState) => {
const [state, dispatch] = useReducer(reducer, initialState);
const setState = (newState) => dispatch(newState);
return [state, setState];
};
function reducer(state, action) {
return typeof action === 'function' ? action(state) : action;
Hi Dimitri,
Thank you so much for writing and providing these clear and detailed articles on React and JavaScript! I've been making a web app and back end in JavaScript and React and so often when I need to answer a question on how things work your website has the best answers. You saved me so much time and headache with your articles. Thank you again!
You're welcome @james-transfire! Thanks for the nice words.
Really nice explanation. Thanks.
Hello Dmitri,
This article is very clear. Thank you for that! I think I got it. I made my custom useState and pasted it below.
I thought using the switch statement was overboard, so I didn't use it. Also, I know many of my lines of code are not necessary, but I decided to still add them to clearly communicate to other developers what I was trying to do in as easy-to-understand as I could think of. Any advice on how to improve it would be great :)
const _useState = initialState => {
const reducer = (currentState, action) => {
const { type, payload} = action;
const {cb, value} = payload;
const updatedState = type === 'function' ?
cb(currentState) :
value;
return updatedState;
};
const [state, dispatch] = useReducer(reducer, initialState);
let setStateTo = input => {
const type = typeof input;
const key = (type === 'function' ? 'cb' : 'value'); //cb: callback fn
const payload = { [key]: input };
dispatch({ type, payload });
};
return [state, setStateTo]
};
Also, if anyone would like to a junior Front-End React/JS Developer, please let me know. I'm looking for new opportunities.
Hi, I'm new to React and was going through your article to get some information on the useReducer hook. Just a question though, on the code demo, shouldn't the "case 'reset'" be returning 'initialState' ? And if no, why ? Thanks
Loved it. strict to point with simple examples
Great article. Very clear explanation!!
@panzerdp
would you please tell me what this code does ?
return () => { clearInterval(idRef.current); idRef.current = 0; };
@zackniyokwizera The code that you mention is the cleanup function of useEffect() hook.
This code will stop the watch if the component unmounts.
Dimitri correct me if I'm wrong but I don't see in the app any component to unmount. No component of the app disappears from the screen after the initial render. I think that the clean up function is called after the stop and the reset button clicked, after the rerender of component and before the callback in useEffect called, because in this case the depedency of useEffect state.isRunning is updated.
My solution in the Challenge:
my custom hooks is useFakeUseState.js:
import { useReducer } from "react";
const useFakeUseState = (initialState) => {
const reducer = (state, action) => {
switch (action.type) {
case "updateState":
return action.newState;
}
};
const [state, dispatch] = useReducer(reducer, initialState);
const setState = (newState) => {
if (typeof newState === "function") {
//if new state given literally
dispatch({
type: "updateState",
newState: newState(state),
});
}else{
//if new state given functionally
dispatch({
type: "updateState",
newState: newState,
});
}
};
return [state, setState];
};
export default useFakeUseState;
Here is a live demo.
customhook import { useReducer, } from "react"; // console.log('>>>>>', useReducer); const getReducer = (data) => (state, action) => { // eslint-disable-next-line default-case switch (action.type) { case "start": return { ...state, isRunning: true, status: 'started' }; case "tick": return { ...state, timer: state.timer + 1, status: 'running' }; case "reset": return { ...state, isRunning: false, timer : 0, status: 'reset' }; case "stop": console.log('stopped'); return{...state, isRunning: false, status: 'freeze'}; case 'clear': return { ...state, status: '' };
} };
export function useForm(initialState, data = {}) { const [state, dispatch] = useReducer(getReducer(data), initialState); const handleStart = () => () => dispatch({ type: "start" });
const handleReset = () =>()=> { dispatch({ type: "reset"}); }; const handleStop = ()=> ()=>{ console.log('handle stop') dispatch({type:"stop"}) } const onTick =()=>{ dispatch({type:"tick"}) }
return { state, handleStart , handleReset, handleStop, onTick}; }
import "./App.css"; import { useEffect, useRef } from "react"; import { useForm } from "./hooks/use-formhook"; let initialState = { isRunning: false, timer: 0, }; // eslint-disable-next-line react-hooks/rules-of-hooks
function App() { const idRef = useRef(0); const { state, handleStart, onTick, handleReset, handleStop } = useForm( initialState );
useEffect(() => { console.log("state12", state); if (!state?.isRunning) { return; } idRef.current = setInterval(() => onTick(), 1000); return () => { clearInterval(idRef.current); }; }, [state?.isRunning]); return (
); }
export default App;
Hello Dimitri,
Thanks for the excellent article!
A doubt: it seems that we need to clear the timer (interval) every time we stop the clock inside the useEffect, isn't it? Otherwise, we will define a new interval every time we turn on (start) the clock. I wanted to say this:
useEffect(() => {
if (!state.isRunning) {
clearInterval(idRef.current); // <---HERE
return;
}
...
What do you think?
Oh, sorry, is DMITRI. I have a cousin named Dimitri :)
Отличный пост! Особенно пример с моторным телеграфом!) Спасибо
One of the best content out there! Compliments Dmitri! Keep writing! I love your work
An Easy Guide to React useReducer() Hook
How to use React useReducer() hook to manage complex state: initial state, action object, dispatch, reducer.
https://dmitripavlutin.com/react-usereducer/