Stop Abusing Client State Management

Rate this content
Bookmark

A lot of React applications out there run for state management solutions such as Redux or Mobx and use it mostly for server side state like 'isLoading', 'isError', etc. We need to stop mixing between server state and client state. Don't get me wrong, client state is important, but 70% of the state is actually a server state. In this talk I will demonstrate how we can encapsulate server state using our own custom hook or using a (perfect) solution such as react-query.

21 min
21 Jun, 2022

Video Summary and Transcription

This Talk discusses state management abuse and the use of React Query for API handling in React applications. The speaker demonstrates implementing loading indicators, custom hooks, caching mechanisms, and introduces React Query as a powerful tool for fetching, caching, and loading data. The conclusion emphasizes that React Query simplifies API handling without the need for complex state management tools like Redux or MobX.

Available in Español

1. Introduction

Short description:

Hi everyone. My name is Nir. I'm 34 years old from Israel, married to a frat and father to three little wonderful girls. I've built web apps since I was a kid. Currently I run a consulting company in webapps development, both in the world of web 2.0 and web 3.0.

Hi everyone. My name is Nir. I'm 34 years old from Israel, married to a frat and father to three little wonderful girls, Baar and Adas. I've built web apps since I was a kid. My first website was the Biggest Israeli Pokemon Fans website and it was a great project. Since then I had my BSc in Computer Science at the Hebrew University of Jerusalem. I worked as a full-stack at Intel for a couple of years. Afterwards I was a frontend team leader at Curv, a blockchain startup which acquired by PayPal. Currently I run a consulting company in webapps development, both in the world of web 2.0 which you're all familiar with, and also in the web 3.0 world.

2. State Management Common Abuse

Short description:

Let's talk about state management common abuse. We choose our favorite state management tool, connect components to backend API functionality, and start using state management awesomeness. I created a React application, declared an API function called fetchPokemon, and used a Pokemon API service to fetch relevant data. I added a two-second delay, fetched the data, and implemented the UI to display the Pokemon data and a button to fetch random Pokemon. Finally, I added some styling to the UI.

Enough about me, let's talk about state management common abuse. So we've all been there, starting a React application, choosing our favorite state management tool, like Redux or Morbix, or many others out there, and connecting our components using these tools to our backend API functionality and start using state management awesomeness.

Let's see it in action. I created a simple React application using code sandbox, and I'll start by declaring an API function called fetchPokemon, which will get in the parameters the Pokemon name, and we will use a Pokemon API service to fetch the relevant Pokemon data. I wrapped it with a promise to make sure we get some delay in the response to mock a common behavior of API functions. So there's a setTimeout, and I set it later to two seconds of delay.

Now I'll do the fetching. We have the Pokemon name, handle the response, which is in a JSON format, so I'll use the JSON, and return the data. Cool. Great. Let's add the two seconds delay I talked about, and now I can call the endpoint in the UI, declaring the state PokemonData and setPokemonData using the useState hook, and creating a fetch random Pokemon function so the app won't be boring.

So I'll be using or I'll be choosing out of three common Pokemons, Pikachu, Charizard, and Squirtle. We will use some random logic using the math library, so multiplying math random with the array length minus 1 and round it up. And now I can use it. I'm calling the fetchPokemon with the chosen Pokemon name. Cool. Return the data. Great. And now for the best part, the UI. So I will declare a div container and display the Pokemon data in a div and create a button for using the fetch function. So in the div, I will display the Pokemon name as well as its image using the sprites data which we get from the Service API. Put it in the image source. And great! Back to the button. I will attach the fetch random Pokemon, which we implemented. And let's add some text. And let's add some alternative text to remove the annoying warning of the Kot Sandbox. Add some typo here. And test it. Cool! We've got Squidward. Now let's add some style, just to move the text a little bit up above.

3. Implementing Loading Indicator

Short description:

And we are done! We've got Toysard, and Squirtle, and Pikachu. I will use user reducer for simplicity reasons. Let's add a state for error data and a success state. Let's adapt the UI to use our new state variable. Implement the loading indicator easily. Fix the bug in the SetTimeout.

And we are done! We've got Toysard, and Squirtle, and Pikachu. Great! I think to myself, an app is not an app without some loading indicator. That's the part where most developers will use Redux or some other solution to hold these kind of states.

I will use user reducer for simplicity reasons, which is very similar to Redux reducers. So I'll create a Pokemon Reducer, getting a state and an action, and switching the action type. So first let's handle a default case, throw in some error, and now add the common loading state. Status is loading, and great. But what if I get an error? So let's add a state for this as well, with the error data, and surely we need a success state to hold the data getting from the Service API. And since we can be sometimes not in neither of these states, let's add an idle state and declare an initial state to begin with.

Now I can just use it in the application, instead of the regular useState. So extracting the state and the dispatch function out of the reducer, using the PokémonReducer and the initial state. And let's adapt the UI to use our new state variable. So when I'm idle, I will display some text, click to fetch a Pokémon, and on success I want to display the Pokémon data. So let's implement this as well. Pay attention that Pokémon data doesn't exist anymore, so we'll replace it with the state variable. And on error, I want to display the error message, which I saved in the state. And finally, I want to disable the button while I'm on loading state.

Now I can finally implement the loading indicator easily. So what I will do, I will switch the button text to loading when I'm in the status of loading. And display the FetchPokemon text when I'm not. Fetching. Okay, and don't forget to dispatch the right state when loading. And when receiving the data. With the data. And when encountering some error. Let's hit the refresh and test it. And I see we don't get the loading indicator. So I think I know why we have some bug in the SetTimeout. Let's fix that. Great.

4. Implementing Custom Hook

Short description:

Test again and we got this. Just the style. We missed the style. So let's move the style to the inner div we implemented. Usually for this kind of async function, developers write different reducers, but I know a better way to share some logic. Custom hooks. Let's create one. Let's take the reducer, move it down and declare a custom hook named userSync. And now let's create a function called execute that will be responsible for changing the state using the dispatch.

Test again and we got this. Just the style. We missed the style. So let's move the style to the inner div we implemented. And great. Test again. Cool.

Usually for this kind of async function, developers write different reducers, but I know a better way to share some logic. Custom hooks. So let's create one. So let's take the reducer, move it down and declare a custom hook which I will name userSync. Okay. The userSync will get an async function. And actually, we can use our reducer. Let's just rename it to something more generic. So fetchReducer might be better. And we can use it in our hook. Great.

And now let's create a function that will be responsible to changing the state using the dispatch. So we'll name it execute. And this function will get parameters of course. And start by dispatching the loading state. Then we will call the async function argument we got with the params. And onSuccess, dispatch the success state. We have the data, of course. And last but not least, when getting an error, we will dispatch the error state, similar to what we did earlier. Quit error with the error data. Now I will have the execute function with use callback to avoid re-renders, except the time, of course, when the function does change. And finally we return from the hook the execute function and the state, which we can use in the application. And let's actually use it.

5. Extracting Execute and State

Short description:

Let's extract execute and state out of the user sync custom hook and pass it our function, fetchRandomPokemon. This pattern is not my invention but rather something I found in a library called user sync, which is implemented very similarly to what we did. It's an amazing library with many other custom hooks.

So let's extract execute and state out of the user sync custom hook, and we need to pass it our function, so fetchRandomPokemon. And let's make some adjustment. We move in the dispatch and move in this function out of the application. Call in execute. Great. And this is working nice. So actually I didn't invent any of that. I just use a pattern I met in the school library, use hooks, and you can see here user sync, which is implemented very similar to what we did. And it's actually amazing and this library has many other custom hooks. I really suggest checking it out.

6. Implementing Cache Mechanism

Short description:

Let's add a cache mechanism to store the fetched data and implement caching. If the data exists in the cache, it will be returned. Otherwise, it will be fetched using an async function and saved in the cache. We can also add invalidation to reset the cache when the data is not up to date. This allows us to re-fetch the data when necessary, such as when a timer is connected.

So with your permission and to make some point, I'm getting rid of the Pokemon randomness for a while and fetching only the great Pikachu. Notice that I am fetching the data every time I click, even though I have the same data all the time. Pikachu. So I think it will be nice to add some cache mechanism. I'll add a cache variable which will hold the data and add some flag named with cache and let's call it with true. And let's implement the caching. So if the data does exist, I will return it from the cache. Right, if it exists. And otherwise, I will fetch it using the async function and save it in the cache. Great. Let's test it. Now it's fetching, and for the second time and third time and so on, it won't fetch again because it's using the cached data. And let's add some invalidation to make sure we support when the data is not up to date. So if invalidate flag is true, I'm resetting the cache. Let's add it to the dependencies array. And now let's set the flag to true. And if I click over and over again, I'm fetching. This is good when I want to tell my custom hook that the data is not relevant anymore and I want to re-fetch it. So I can connect a timer to it or something like that.

7. Introducing React Query

Short description:

React Query is my first choice for dealing with API LL. It provides a built-in hook called Use Query that includes everything we implemented in UserSync. We can replace UserSync with Use Query, passing the Query key and query function as parameters. The result includes data, error indicators, and a refresh function. With React Query, we get caching out of the box.

And you know what? It's so cool to add some retry mechanism. And I think you know where I'm going with it. We don't have to work this hard, although it is fun. So I want you to meet React Query, which is my first choice when dealing with the API LL.

So first I'll wrap it up real quick with the React Query provider. Coming from the React Query library, which I just installed. And wrapping the entire application with the provider and the client. And now I can use the built-in hook from React Query called Use Query. So this hook actually holds everything we just implemented in UserSync, including the cache system, the retry mechanism, the invalidation, and many other cool functionalities.

We can get rid of the UserSync and let's use Use Query instead. It has two parameters, actually. The first one is the Query key, which serves as the id of the query. And the second one is the query function, as similar as the UserSync hook we just implemented. So, this will be the FetchPokemon function, currently with Pikachu. And let's extract everything out of the result. So we have the data. We have some indicators, like if there was an error, if it's loading, and so on. And let's use it in the UI, make some adjustments. Great, the error. We can remove this and use the IsError flag, which we got for free. And the error variable, which we can extract as well. And the refresh function, which will replace the execute from the user's sync hook. Great, isLoading here instead. And here as well. Not IsLoading. Cool. And great, we got the caching out of the box. Let's clear things out. All of this. Oh, this is such a good feeling.

8. Conclusion and Recommendation

Short description:

And we can even use the fetch random Pokemon once again and get in the cache. So here is the React Query library website. It has some cool documentation and functionality that makes handling the API layer simple. In conclusion, we connected a simple API function to a simple UI. As the UI grows, handling API functionality becomes more complicated. We don't need fancy tools like Redux or MobX for server state handling. React Query is a helpful solution for fetching, caching, and loading in every React application.

And we can even use the fetch random Pokemon once again and get in the cache. Great. Everything works.

So here is the React Query library website. Which is very great. It has some cool documentation. And I really suggest going over it. It has so much functionality which is useful. And really makes things very simple relating to handling the API layer. So it's really worth checking.

So in conclusion, what we did here was taking a simple API function and connecting it to a really simple UI. But as you can imagine, as the UI gets bigger and bigger, it gets more and more complicated to handle this kind of API functionality. And we don't need fancy tools such as Redux, MobX or many others out there to handle what is called server states. These tools are meant for client state operations such as if the dialog is open, if a menu bar is open, if I'm logged in or not logged in. This is client state. In any case of server state handling, such as fetching, caching, loading and so on, I really suggest either build some similar solution to use query or use async, custom hooks we just saw or really consider using React query which is really, really helpful in actually every application, every React application I saw so far. So hope you had a great time.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Vue.js London Live 2021Vue.js London Live 2021
34 min
Everything Beyond State Management in Stores with Pinia
Top Content
When we think about Vuex, Pinia, or stores in general we often think about state management and the Flux patterns but not only do stores not always follow the Flux pattern, there is so much more about stores that make them worth using! Plugins, Devtools, server-side rendering, TypeScript integrations... Let's dive into everything beyond state management with Pinia with practical examples about plugins and Devtools to get the most out of your stores.
React Advanced Conference 2022React Advanced Conference 2022
30 min
Using useEffect Effectively
Top Content
Can useEffect affect your codebase negatively? From fetching data to fighting with imperative APIs, side effects are one of the biggest sources of frustration in web app development. And let’s be honest, putting everything in useEffect hooks doesn’t help much. In this talk, we'll demystify the useEffect hook and get a better understanding of when (and when not) to use it, as well as discover how declarative effects can make effect management more maintainable in even the most complex React apps.
React Summit Remote Edition 2020React Summit Remote Edition 2020
30 min
React Query: It’s Time to Break up with your "Global State”!
Top Content
An increasing amount of data in our React applications is coming from remote and asynchronous sources and, even worse, continues to masquerade as "global state". In this talk, you'll get the lowdown on why most of your "global state" isn't really state at all and how React Query can help you fetch, cache and manage your asynchronous data with a fraction of the effort and code that you're used to.
React Day Berlin 2022React Day Berlin 2022
22 min
Jotai Atoms Are Just Functions
Top Content
Jotai is a state management library. We have been developing it primarily for React, but it's conceptually not tied to React. It this talk, we will see how Jotai atoms work and learn about the mental model we should have. Atoms are framework-agnostic abstraction to represent states, and they are basically just functions. Understanding the atom abstraction will help designing and implementing states in your applications with Jotai
React Advanced Conference 2023React Advanced Conference 2023
28 min
A Practical Guide for Migrating to Server Components
Server Components are the hot new thing, but so far much of the discourse around them has been abstract. Let's change that. This talk will focus on the practical side of things, providing a roadmap to navigate the migration journey. Starting from an app using the older Next.js pages router and React Query, we’ll break this journey down into a set of actionable, incremental steps, stopping only when we have something shippable that’s clearly superior to what we began with. We’ll also discuss next steps and strategies for gradually embracing more aspects of this transformative paradigm.
JSNation 2022JSNation 2022
27 min
Announcing Starbeam: Universal Reactivity
Starbeam is a library for building reactive data systems that integrate natively with UI frameworks such as React, Vue, Svelte or Ember. In this talk, Yehuda will announce Starbeam. He will cover the motivation for the library, and then get into the details of how Starbeam reactivity works, and most importantly, how you can use it to build reactive libraries today that will work natively in any UI framework. If you're really adventurous, he will also talk about how you could use Starbeam in an existing app using your framework of choice and talk about the benefits of using Starbeam as the state management system in your application.

Workshops on related topic

React Summit 2020React Summit 2020
96 min
Rethinking Server State with React Query
Top Content
Featured Workshop
The distinction between server state and client state in our applications might be a new concept for some, but it is very important to understand when delivering a top-notch user experience. Server state comes with unique problems that often sneak into our applications surprise like:
- Sharing Data across apps- Caching & Persistence- Deduping Requests- Background Updates- Managing “Stale” Data- Pagination & Incremental fetching- Memory & Garbage Collection- Optimistic Updates
Traditional “Global State” managers pretend these challenges don’t exist and this ultimately results in developers building their own on-the-fly attempts to mitigate them.
In this workshop, we will build an application that exposes these issues, allows us to understand them better, and finally turn them from challenges into features using a library designed for managing server-state called React Query.
By the end of the workshop, you will have a better understanding of server state, client state, syncing asynchronous data (mouthful, I know), and React Query.
React Summit Remote Edition 2021React Summit Remote Edition 2021
71 min
State Management in React with Context and Hooks
WorkshopFree
A lot has changed in the world of state management in React the last few years. Where Redux used to be the main library for this, the introduction of the React Context and Hook APIs has shaken things up. No longer do you need external libraries to handle both component and global state in your applications. In this workshop you'll learn the different approaches to state management in the post-Redux era of React, all based on Hooks! And as a bonus, we'll explore two upcoming state management libraries in the React ecosystem.