Coder Social home page Coder Social logo

Comments (5)

DioNNiS avatar DioNNiS commented on May 21, 2024 3

useReducer is beneficial when dealing with complex state logic and multiple sub-values; a very deep object for example. As the documentation says, it makes testing and debugging easier.

I'm trying to convey that for the described use case, useState does a better job than useReducer. Please see my example in the first message.

My proposal is to deprecate useReducer and provide more examples for useState.

from react.

rickhanlonii avatar rickhanlonii commented on May 21, 2024 1

If we were going to remove anything, it would be useState, because useState is basically a helper for useReducer to handle the basic cases. In fact, useState is implemented with useReducer under the hood, with a basic reducer provided.

What you've provided is essentially a reducer. The example you give can be replaced with useReducer, which is closely equivalent to what useState is doing:

const myModule = {
  increment: (state) => {
    return {count: state.count + 1};
  },
    
  decrement: (state) => {
    return {count: state.count - 1};
  }
};

const reducer = (state, action) => {
  return action(state);
} ;
const Counter = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch(myModule.increment)}>Increment</button>
      <button onClick={() => dispatch(myModule.decrement)}>Decrement</button>
    </div>
  );
};

But both this and the useState example are annoying because now you'll need to pass both the dispatch and the myModule functions around to children. You can solve this by wrapping the dispatches:

const myModule = {
  increment: (state) => {
    return {count: state.count + 1};
  },
    
  decrement: (state) => {
    return {count: state.count - 1};
  }
};

const reducer = (state, action) => {
    return action(state);
};
const Counter = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  const dispatcher = useMemo(
    () => ({
      increment: () => dispatch(myModule.increment),
      decrement: () => dispatch(myModule.decrement),
    }),
    []
  );

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatcher.increment()}>Increment</button>
      <button onClick={() => dispatcher.decrement()}>Decrement</button>
    </div>
  );
};

But that's a lot of indirection to follow. It's less indirection to write as typical reducer:

const reducer = (state, action) => {
    switch (action) {
      case "increment":
        return { count: state.count + 1 };
      case "decrement":
        return { count: state.count - 1 };
      default:
        throw Error('Unknown action: ' + action.type);
    }
  };
const SCounter = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch("increment")}>Increment</button>
      <button onClick={() => dispatch("decrement")}>Decrement</button>
    </div>
  );
};

And if you want to pass more values to the action, the convention is to use type on the action:

const reducer = (state, action) => {
    switch (action.type) {
      case "increment":
        return { count: state.count + 1 };
      case "decrement":
        return { count: state.count - 1 };
      default:
        throw Error('Unknown action: ' + action.type);
    }
  };
const Counter = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({type: "increment"})}>Increment</button>
      <button onClick={() => dispatch({type: "decrement"})}>Decrement</button>
    </div>
  );
};

Of course, if you want to use functions, you can still wrap them:

const reducer = (state, action) => {
    switch (action.type) {
      case "increment":
        return { count: state.count + 1 };
      case "decrement":
        return { count: state.count - 1 };
      default:
        throw Error('Unknown action: ' + action.type);
    }
  };
const Counter = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

 const {increment, decrement} = useMemo(() => {
   return {
     increment: (...args) => dispatch({type: "increment", ...args}),
     decrement: (...args) => dispatch({type: "decrement", ...args}),
   }
 }, []);
 
  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => increment()}>Increment</button>
      <button onClick={() => decrement()}>Decrement</button>
    </div>
  );
};

But notice that there's still less indirection than before. This also scales up better because the single reducer function operates as a function that accepts state, and an action and returns the same state object. The "switch" here is a benefit because you're able to see a list of all the actions that can be taken on the state, and the result of the action. For complex state objects, the function example may be difficult to follow and result in hard to see bugs, because essentially each function in your object is a separate reducer.

from react.

TheJarX avatar TheJarX commented on May 21, 2024

Hi,

useReducer is beneficial when dealing with complex state logic and multiple sub-values; a very deep object for example. As the documentation says, it makes testing and debugging easier. So for small and simple states useState does the job. If you're incrementing the complexity of your state you should start thinking in useReducer

Anyway I believe this sort of questions have a better place: https://react.dev/community

from react.

TheJarX avatar TheJarX commented on May 21, 2024

I mean, yeah got your point. What I tried to say was that your concrete use case was simple enough to use useState instead of useReducer. No need to deprecate it; just don't use it if you don't need it. Both hooks serve different purposes. As you can see in the source code, they do work in a similar way but with a couple of differences.

You may want to check the documentation for more useReducer examples in order to catch the idea of its purpose:

Regarding to "...provide more examples for useState". It's up to you how you're going to use useState, I'm sure there're a lot of cool recipes out there (including yours). I mean you can make an API request in the initial state; doesn't mean you should do it tho ¯_(ツ)_/¯.

from react.

harsha-n avatar harsha-n commented on May 21, 2024

Conside if you are building an ecommerce site or something more complex, at that point of time the useReducer will be of more use than useState. If you consider a simple increment, decrement separate module will be better option. Ensuring that the state remains consistent and predictable across all actions and components can be difficult with a separate module, especially as the application grows and evolves over time

from react.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.