Advanced State Management

Context API

Context is React's own state mangement API. It provides a way to create global state that can be accessed by any component within the context provider.

const MyContext = React.createContext();

const MyProvider = ({ children }) => {
  const [value, setValue] = React.useState("some state");

  return (
    <MyContext.Provider value={{ value, setValue }}>
      {children}
    </MyContext.Provider>
  );
};

const MyComponent = () => {
  const { value, setValue } = React.useContext(MyContext);
  return <div>{value}</div>;
};

// Usage
<MyProvider>
  <MyComponent />
</MyProvider>

Jotai

Jotai allows you to share state by reference, you get the reference, you get the state, very intuitive.

import { atom } from 'jotai'
import { useAtomValue, useSetAtom } from 'jotai'

const countAtom = atom(0)

const Counter = () => {
  const count = useAtomValue(countAtom)
  return (
    <div>{count}</div>
  )
}

const IncCounter = () => {
  const setCount = useSetAtom(countAtom)

  return (
    <button onClick={() => {setCount(count => count + 1)}}>
      Inc
    </button>
  )
}

const CountApp = () => {
  return (
    <>
      <Counter />
      <InCounter />
    </>
  )

Redux

Redux is one of the oldest state management solution for react. It's still widely used, especially in larger scale applications.

digraph Redux {
    rankdir=LR;

    node [
      shape=rectangle,
      color="#eeeeee",
      style=filled,
      fontcolor=black,
      fontsize=14,
      fontname="Monospace"];
    
    Store [label="Store" fillcolor="lightyellow"];
    Actions [label="Actions" fillcolor="lightyellow"];
    Reducers [label="Reducers" fillcolor="lightyellow"];
    View [label="View" fillcolor="lightyellow"];

    View -> Actions [label="dispatch" color="#555555"];
    Actions -> Reducers [label="sent to" color="#555555"];
    Reducers -> Store [label="returns new state" color="#555555"];
    Store -> View [label="provides state" color="#555555"];
}

Reducers and actions in Redux promots a clear separation of concerns. Actions define the "what" of state changes, while reducers handle the "how," making the flow predictable and easier to debug.

This structure improves maintainability, facilitates testing, and allows for easier scaling of applications. Ultimately, it leads to a more organized codebase, making it easier to manage complex state interactions. But it might feel overengineered for small projects.

import { connect } from 'react-redux';

const Counter = ({ count, increment }) => (
  <div>
    <p>{count}</p>
    <button onClick={increment}>Increment</button>
  </div>
);

const mapStateToProps = (state) => ({
  count: state.counter,
});

const mapDispatchToProps = (dispatch) => ({
  increment: () => dispatch({ type: 'INCREMENT' }),
});

export default connect(mapStateToProps, mapDispatchToProps)(Counter);

Action Middleware

Action middleware in Redux is a way to extend the store's capabilities by intercepting actions before they reach the reducer. This allows you to perform side effects, such as asynchronous operations or logging, without cluttering your components or reducers. Common examples include Redux Thunk for handling asynchronous actions and Redux Saga for managing complex side effects. Middleware enhances flexibility and keeps the codebase clean and organized.

Time Travel

One advantage of global state management over solutions like Jotai is that it allows for comprehensive inspection and manipulation of the entire application state and its history. This capability can greatly enhance debugging, testing, and overall comprehension of the application.

Redux Time Travel is a tool for Redux developers that enables exploration of the history of Redux applications, providing valuable insights into their behavior.