Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow createReducer to be used without initial state #259

Open
lnhrdt opened this issue Apr 9, 2022 · 6 comments
Open

Allow createReducer to be used without initial state #259

lnhrdt opened this issue Apr 9, 2022 · 6 comments

Comments

@lnhrdt
Copy link

lnhrdt commented Apr 9, 2022

We are using createReducer throughout a React application. We always use these reducers with React's useReducer hook. In this scenario it is cumbersome to supply the initial state to both createReducer and to useReducer. It's especially cumbersome when the initial state is initialized by the runtime context – I can build the state at runtime and pass it to useReducer, but then the initial state passed to createReducer is not used. Understanding which initial states are real and which ones are just placeholders that will never be used has been a source of confusion for our team.

Ideally we could just rely on the type parameter passed to createReducer to build a reducer rather than also having to supply an initial state that may never be used.

@piotrwitek
Copy link
Owner

piotrwitek commented Apr 13, 2022

Hey @lnhrdt, thanks for sharing your feedback. Could you please provide the usage examples for your use case so I could fully understand what is the source of confusion. Thanks

Also please understand this library wasn't designed to be used in such a way but let me see so I can explain what is the root issue here and perhaps work out a new developer friendly utility that would be designed for such usage.

@lnhrdt
Copy link
Author

lnhrdt commented Jun 5, 2022

Hello @piotrwitek! Thanks for getting back to me and apologies for the delay. Here's an annotated example.

import React, {Reducer, useReducer} from 'react'
import {ActionType, createAction, createReducer, getType} from 'typesafe-actions'

type State = {
  height: number
}

const actions = {
  addHeight: createAction('ADD_HEIGHT')<number>(),
  doubleHeight: createAction('DOUBLE_HEIGHT')(),
}

type Action = ActionType<typeof actions>

/*
 * We don't need to declare an initial state with this reducer.
 * The useReducer hook requires it anyway down below, so this is good.
 * */
const switchReducer: Reducer<State, Action> = (prevState, action) => {
  switch (action.type) {
    case getType(actions.addHeight): {
      return {
        height: prevState.height + action.payload
      }
    }
    case getType(actions.doubleHeight): {
      return {
        height: prevState.height * 2
      }
    }
  }
}

const AppWithSwitchReducer = () => {
  /*
  * Using React's useReducer here, we are required to provide the initial state.
  * */
  const [state, dispatch] = useReducer(switchReducer, {height: 0})
  return (
    <div>
      <div>height: {state.height}</div>
      <button onClick={() => dispatch(actions.addHeight(5))}>add 5</button>
      <button onClick={() => dispatch(actions.doubleHeight())}>double</button>
    </div>
  )
}

/*
 * This builder patter is super cool, compact, and readable!
 * But there is no way to use it without providing an initial state.
 * This is unfortunate because while in this example the state is simple,
 * in realistic cases the state is very complex and having initial state in 2 places is confusing.
 * I wish we could use the builder pattern without providing an initial state here.
 * */
const builderReducer: Reducer<State, Action> = createReducer<State, Action>({height: 0})
  .handleAction(actions.addHeight, (state, action) => ({height: state.height + action.payload}))
  .handleAction(actions.doubleHeight, (state) => ({height: state.height * 2}))

const AppWithBuilderReducer = () => {
  /*
  * Again, React's useReducer requires us to provide the initial state.
  * */
  const [state, dispatch] = useReducer(builderReducer, {height: 0})
  return (
    <div>
      <div>height: {state.height}</div>
      <button onClick={() => dispatch(actions.addHeight(5))}>add 5</button>
      <button onClick={() => dispatch(actions.doubleHeight())}>double</button>
    </div>
  )
}

Does that help clarify the way I'm trying to use your library?

@lnhrdt
Copy link
Author

lnhrdt commented Aug 25, 2022

Hello @piotrwitek were you able to read through my example or do you have any questions?

@piotrwitek
Copy link
Owner

Hey @lnhrdt thanks a lot for clarification, it's clear! I just missed your notification. I will have a look this week, but I agree with you that this is a generic pattern that could be a nice fit into the library. Let me check the details so I can prepare a proposal for such a feature.

@lnhrdt
Copy link
Author

lnhrdt commented Feb 27, 2024

Believe it or not, we're still using the library and still wishing for this feature.

Any chance of continued development in this project @piotrwitek, or have you moved on?

@lnhrdt
Copy link
Author

lnhrdt commented Mar 28, 2024

Hey @piotrwitek! We ended up moving to a new solution in our application. I thought I'd mention it here in case you ever come back to this issue–we've already moved on and wouldn't benefit from the feature anymore.

Thank you for this library, regardless. We used it happily for a long time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants