question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Discussion: declarative side effects and data fetching approaches

See original GitHub issue

Currently, Redux Toolkit adds thunks as the default supported approach for any kind of async / side effect behavior, largely because it’s the simplest possible approach that works, and because it’s also the most widely used Redux async middleware.

That’s a large part of why I’m considering adding some kind of a createAsyncThunk API to abstract common data fetching and action dispatching logic. It matches what we already have built in to RTK, and what many Redux users are already doing.

@davidkpiano has strongly argued that side effects should be declarative, instead, possibly along the lines of the redux-loop store enhancer.

I’m open to discussing possible APIs and approaches here. My initial point of concern is that this is a very different kind of API than most Redux users are used to, and it would add an extra level of teaching and documentation to explain.

Discuss!

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:2
  • Comments:47 (6 by maintainers)

github_iconTop GitHub Comments

8reactions
nordfjordcommented, Sep 4, 2020

I don’t feel particularly qualified, but I’d like to drop my 2 cents.

I don’t think redux-loop fits well with the current status of RTK. Does returning a loop(state, cmd) even work with immer?

So what if we had an RTK native way to “queue effects” what might that took like

Here’s a half thought out idea: an effects object baked into createSlice.

const slice = createSlice({
  name: 'user',
  initialState: {status: 'idle'},
  reducers: {
    gotUser(state, action) {
     // ...
    },
    getUser(state, action) {
      // ...
      
      slice.effects.fetchUser()
    },
    getUserFromInput(state, action) {
      // ...
     
      slice.effects.handleInput(action.payload)
    }
  },
  effects: {
    async fetchUser() {
      const user = await fetchUser()
      return slice.actions.gotUser(user)
    },
    async handleInput(input, canceled) {
      await wait(300)
      if (canceled()) return
      const user = await fetchUser(input)
      return slice.actions.gotUser(user)
    }
  }
})

The effects object is a Record<string, (...args: any[])=> Action | Promise<Action>>

calling slice.effects[x] does not immediately call the function defined below, it instead puts the effect on an effect queue which is processed after the reducer is done.

If the same effect function is called while still being processed it’s automatically canceled.

POC Here

I think this works only because it’s baked into createSlice, it wouldn’t work as a traditional middleware.

I think the benefits of this approach are:

  1. It feels pretty natural, “oh I need an effect, I’ll just call it right here.”
  2. Gives some benefits over thunks (like cancellation) without needing too much knowledge
  3. Colocates state, actions, and effects

And to be clear I’m not suggesting this as an API.

I’m just trying to broaden the discussion beyond “What middleware is best,” because with toolkit there’s an opportunity to use createReducer/createSlice to bake effects into the mix.

7reactions
AlexeyDemedetskiycommented, Feb 10, 2020

I have tried Declarative Side-Effects many times and always fail to scale them. Whole system is become too fragile to accept changes.

I have found that Data-Driven Side-Effects works instead. For the short:

  1. Reducers update the state based on actions.
  2. Side effect interpreter perform side effects based on the state.
  3. That’s it.

In some sense UI is just another side effect.

Read more comments on GitHub >

github_iconTop Results From Across the Web

React's Effect Hook - Medium
A side effect describes any time a function modifies a value outside of its local scope that is separate from its return value....
Read more >
Performance of frameworks for declarative data fetching
One proposed solution for increasing efficiency in data fetching is through the use of frameworks for declarative data fetching.
Read more >
NextJS / React SSR: 21 Universal Data Fetching Patterns ...
21 Universal Data Fetching Patterns & Best Practices for NextJS ... that mutations will have side-effects on other data-fetching hooks.
Read more >
Fetching Data in React with useEffect - Max Rozen
Fetching data from an API, communicating with a database, and sending logs to a logging service are all considered side-effects, as it's possible...
Read more >
Declarative APIs in an Imperative World - InfoQ
The data only flows one way and this data is called props. React is far from being the only declarative UI framework that's...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found