DEV Community

Joma
Joma

Posted on

useReducer vs useState

Both of these hooks do the same thing... but in a different way. What we write for useReducer hook, the same magic is happening 'behind the scenes' for useState (we see only it's result!).

Let's see the example:

1. useState

    const[contactName, setContactName] = useState("");
    const[contactImage, setContactImage] = useState(null);
    const[contactImageUrl, setContactImageUrl] = useState(null);

    function handleImageUpload(e) {
        const file = e.target.files[0];
        const fileName = e.targe.files[0].name;

        if(!file) return;

        const matchRgx = fileName.match(/\.(jpg|png|jpeg)/);
        if (matchRgx != null) {
            setContactImageUrl(URL.createObjectURL(e.target.files[0]));
        }
        else {
             setContactImage(null);
             setContactImageUrl(null);
        }
    }

    function handleForm(e) {
        e.preventDefault();
        window.alert("Successfully added a contact!");
    }

    return (
        <div>
            <form onSubmit={handleForm}>

                <small>Name:</small>
                <input
                    value={contactName}
                    type='text'
                    onChange={(e)=> setContactName(e.target.value)}
                />

                <small>Image:</small>
                <input
                    type='file'
                    accept='image/jpeg,image/png,image/jpg'
                    onChange={handleImageUpload}
                />

                <img src={contactImageUrl} />

                <button type='submit'>Submit</button>
            </form>
        </div>
    )
}

Enter fullscreen mode Exit fullscreen mode

2. useReducer

const initialFormState = { contactName: '', contactImage:null, contactImageUrl:null };

function reducer(state, action) {
    if (action.type === 'setState') {
        return {
            ...state,
            [action.field]: action.value
        };
    } else if (action.type == "ImageUpload") {
        return {
            ...state,
            contactImage: action.file,
            contactImageUrl: action.url
        }
    }
    return state;
}

const ReducerHook = () => {
    const [state, dispatch] = useReducer(reducer, initialFormState);

    function handleImageUpload(e) {
        const file = e.target.files[0];
        const fileName = e.targe.files[0].name;

        if(!file) return;

        const matchRgx = fileName.match(/\.(jpg|png|jpeg)/);
        if (matchRgx != null) {
            const imageUrl = URL.createObjectURL(e.target.files[0]);
            dispatch({
                type:"ImageUpload",
                file:e.target.files[0],
                url: imageUrl
            })

        }
        else {
             dispatch({
                type:"ImageUpload",
                file:null,
                url: null
            })

        }
    }

    function handleName(e) {
        e.preventDefault();
    }

    return (
        <div>
            <form onSubmit={handleName}>
                <small>Name:</small>
                <input
                    name="contactName"
                    value={state.contactName}
                    type='text'
                    onChange={(e) => {
                        dispatch({
                            type: 'setState',
                            field: e.target.name,
                            value: e.target.value
                        })
                    }}
                />

                <small>Image:</small>
                <img src={state.contactImageUrl} />
                <input
                    type='file'
                    accept='image/jpeg,image/png,image/jpg'
                    onChange={handleImageUpload}
                />

                <button type='submit'>Submit</button>
            </form>
        </div>
    )
}

export default ReducerHook
Enter fullscreen mode Exit fullscreen mode

EXPLANATION:
When using a useState hook, we directly define a setter functions (something, setSomething) and call them for every state we want to track, differently.
However, useReducer has its own setter function which is literally shared among states. That means that we don't need to define more setter functions for every state we want to track. That setter function is called a reducer.
const[state, dispatch] = useReducer(reducer, initialState)

  • state is a container will all the data we want to manipulate with
  • dispatch is communicating with a reducer function by sending instructions about what should be changed and how
  • reducer is the actual function that submits changes
  • initialState is an object with all the initial states

Top comments (0)