Hi,

In today's post I want to present an intro to the Constate Library - an interesting, in my opinion, alternative to managing the global state of the application. 

What is it exactly and where did it come from?  

Constate is a library dedicated to React and React Native. It is an open-source project initiated about 4 years ago by Diego Haz. Currently, according to npmjs.com website, it's downloaded by over 55k users per week. Is it a lot? Not necessarily, however, we must keep in mind that it is a competition to a much larger, more popular and more often used library - Redux. But this has already been tackled on our blog written by Patryk Poleszczuk, so for those of you interested in the topic - here's a link https://www.apptension.com/blog-posts/how-to-use-redux-to-make-your-react-project easier.  

Coming back to the constate itself it is a hybrid of two well-known solutions - React Context and Hook useState. React Context is an integral part of the React library. It is primarily used to allow access for a given component tree to the data we want to pass from the parent component to the child component. Of course, we can pass the data through props by passing them through all the components but in the case of more extensive applications, this approach does not make sense. UseState on the other hand is probably the most basic Hook used in React and React Native. It is used to manage the local state of the component. The simplest usage example would be to define useState like this: const [data, setData] = useState(). What does it mean? The variable data will contain the data we set using the setData function. 

How to use it?  

In general, the concept of using constate is based on creating your Hooks. However, you have to remember that the state will be available only in the tree, where you define the context, so it is safest to define it in the "top" component which is usually App.js / App.tsx. It may seem a problem that for each new Hook that will be responsible for the specified global data, you will have to create a separate context. There is a straightforward solution to this. Well, we can define our function which will use .reduce() to merge all the contexts into one on input, so that in our App.js / App.tsx we will just have to pass our entire NavigationContainer as children to the ConstateProvider. Of course, we don't have to do this for all contexts. If we plan that a given context does not have to cover the whole application, but only a selected tree of components, we can do it in such a way that we only pass a given component as children of the context.  

Some simple examples of how to use this in practice  

Here is the simplest example that contains only the declaration of the context. Calling it in a component looks like this => const {count, setCount} = useCount();

import {useState} from 'react';
import constate from 'constate';

const useCountContext = () => {
	const [count, setCount] = useState('');
  
  return {count, setCount};
};

export const [CountProvider, useCount] = constate(useCountContext);

Of course, in the context constructor itself we can define functions that will process the state. The  use of such functions in components remains the same. 

import {useState} from 'react';
import constate from 'constate';

const useCountContext = () => {
	const [count, setCount] = useState('');
  
  const increment = () => setCount(prevState => prevState + 1);
  
  return {count, setCount};
};

export const [CountProvider, increment, useCount] = constate(useCountContext);

But what if we close the application and lose all our global state? How to remedy this? This is  where AsyncStorage comes to our aid. It allows us to preserve the application state after it is shut  down and restarted. The only problem which occurs when you define AsyncStorage properly is to  specify the correct identifiers for the stored data. They have to be unique in the whole application.

import {useState, useEffect} from 'react';
import constate from 'constate';
import AsyncStorage from '@react-native-async-storage/async-storage';

const useCountContext = () => {
	const [count, setCount] = useState<string>('');
  
  const increment = () => setCount(prevState => prevState + 1);
  
  useEffect(() => {
  	AsyncStorage.getItem('UNIQUE_DATA_ID').then((value: string | null) => {
    	value && setCount(value);
    });
  }, []);
  
  useEffect(() => {
  	AsyncStorage.setItem('UNIQUE_DATA_ID', count);
  }, [count]);
  
  return {count, increment, setCount};
};

export const [CountProvider, useCount] = constate(useCountContext);

Summary 

Constate is a little known and in my opinion underestimated alternative for managing the global  state of the application. Of course in the process of project planning many factors have to be  taken into account when choosing the technology and it may turn out that e.g. Redux will be  much better suited to the needs of the application. However, this library is constantly developed,  current and, in my opinion, worth attention in the process of selecting technology in the project.  As it is based on Hooks, which are currently used by almost all developers using React, it is very  easy to use and gives very interesting possibilities with other libraries based on Hooks like React  Query. But I will tell about this combination next time :)