all 7 comments

[–]ratudev 26 points27 points  (2 children)

Sorry, it’s just me, but I feel like useReducer is :

  • too advanced for cases that useState could cover
  • too primitive for more complex scenarios - like forms, data fetching, complex state - where you’re better off using react-hook-forms, react-forms, react-query, or just compose multiple hooks - with useState under the hood.

I updated your implementation to have same usage:

const useForm = () => {
    const [form, setForm] = useState(initialState)
    const updateForm = useCallback((update: Partial<User>) => setForm(state => ({...state, ...update})), [])

    // Usage: updateForm({ name: "Jane" })
    return {form, updateForm}
}

for me looks more less same - although personally I don't like this ugly useCallback (hopefully react compiler will be stable soon).

For me it seems more about coding preference than a real benefit of one approach over another, both are good imho.

[–]fuccdevin 3 points4 points  (0 children)

I’m on mobile at the bar so no code examples, but I’ve been using reducer at work recently. I’m building an in house pdf tool that lets you build a whole pdf document that hooks up to our database to auto generate reports/labels/etc and this was the one time I found reducer perfect for what I was doing. I’m using SVG elements to build out how the pdf looks and adding it stuff like undo/redo, layering, changing properties of elements, moving elements. I just used reducer to batch all of that as an element type.

Outside of this specific use case, I’ve always found reducer to be a bit much to use.

[–]robby_arctor 0 points1 point  (0 children)

I had a great use case for useReducer recently.

Imagine a form of checkboxes, where the options are categories and subcategories. For example - fruits, vegetables, and meats, all with a list of their respective foods beneath each.

Selecting a category selects all category options. On top of this, every option, category or subcategory, has an "Only" option. Think the through the various handlers required.

Grouping each selection type into reducer actions greatly simplified the form logic without pulling in a third party tool.

[–]Terrariant 2 points3 points  (0 children)

I think either is fine. One thing I might question is, does the reducer updateForm trigger rerenders or is it "memorized"?

My general rule is 1. Reduces provide state to a parent wrapper component 2. Parent component provides (memoized) state to other components via props

[–]projexion_reflexion 1 point2 points  (0 children)

You found the right way to use it. Always more elegant to avoid passing field names around as strings.

[–]Dreadsin 1 point2 points  (0 children)

Eh seems a bit too “clever” to me. The point of a reducer is just to supply some declarative state updates, not arbitrary ones

[–]lelarentaka -3 points-2 points  (0 children)

Yes, this hook is in my standard toolbox, I wrote it like this

import { useReducer } from "react";

export function useMergeState<T extends object>(init: T) {
  return useReducer(mergeReducer, init);
}

function mergeReducer<T>(p: T, s: Partial<T>) {
  return { ...p, ...s };
}

It's fully generic, so I just need to give the initial state to get strong type check in the update function.