React Atom: A simple yet powerful React JS / React Native state management
Tags: react, statemanagement, state, redux
Published: 2023-03-21 10:40:01

---
title: React Atom: A simple yet powerful React JS / React Native state management
published: true
description: A powerful state management for React Js/ React Native Applications
tags: react, statemanagement, state, redux
---

## Mongez React Atom

Mongez React Atom (MRA) is a state management tool to manage data between Project's Components

## Why?

The main purpose of the birth of this package is to work with a simple and performant state management tool to handle data among components.

## Features

The top features of MRA is:

- Lightweight package
- Can be used everywhere, inside or outside React Components.
- Easy to use.
- Easy to learn.
- Power of focus, works with single value only.
- Typescript support.
- Can be used with any other state management tool or even better, use it as a replacement for Redux!
- Works with React JS and React Native.

## Use Cases

The main purpose of a state management is to keep and maintain data, MRA is designed to work with a single value, this gives it a power of control to manage the data and keep it in a single place.

## How it works

The concept is simple, everything works in `atoms`, each atom `MUST` hold a single value, in nutshell every atom has a single responsibility.

## Installation

Yarn

`yarn add @mongez/react-atom`

NPM

`npm i @mongez/react-atom`

Pnpm

`pnpm i @mongez/react-atom`

## Usage

Let's assume we have a currency to be displayed in the header, the footer contain a dropdown to change currency from it, we need to update the header's current currency once the user changes it in the footer without re-rendering the other components in same level or even the parent component that holds the header and footer.

Let's create a new simple atom

`src/atoms/currency-atom.ts`

```ts
import { atom } from '@mongez/react-atom';

export const currencyAtom = atom({
  key: 'currency', 
  default: 'USD', // default value for the atom
});
```

Let's jump to use it in our `Header` component

`src/components/Header.tsx`

```tsx
import { currencyAtom } from 'src/atoms/currency-atom';

export default function Header() {
  // get the current value of the atom
  const currentCurrency = currencyAtom.value;
  return (
    <>
      <div>Current Currency: {currentCurrency}</div>
    </>
  );
}
```

As you can see, the `default` value is just a plain string, the atom initiation requires two keys:

1. `key` the atom's name, it must be unique for each atom, otherwise an error will be raised in the development environment.
2. `default` holds the default value, it can be any type.

Now let's jump to the `Footer` component

`src/components/Footer.tsx`

```tsx
import { currencyAtom } from 'src/atoms/currency-atom';

export default function Footer() {
  return (
    <>
      <button onClick={() => currencyAtom.update('EUR')}>Change to EUR</button>
      <button onClick={() => currencyAtom.update('USD')}>Change to USD</button>
      <button onClick={() => currencyAtom.update('GBP')}>Change to GBP</button>
      <button onClick={() => currencyAtom.update('EGP')}>Change to EGP</button>
    </>
  );
}
```

This will update the currency atom value, but there will no be any re-rendering for the `Header` component, because it's not subscribed to the atom, but if we want to re-render the `Header` component once the currency is changed, we can use `useValue` hook.

`src/components/Header.tsx`

```tsx
import { currencyAtom } from 'src/atoms/currency-atom';

export default function Header() {
  // get the current value of the atom
  // and watch for any changes for the atom value
  // if the atom's value is changed, re-render the component with the new value
  const currentCurrency = currencyAtom.useValue();
  return (
    <>
      <div>Current Currency: {currentCurrency}</div>
    </>
  );
}
```

Now whenever the user clicks on any of the buttons in the footer, the header will be re-rendered with the new value.

You can find an example code here in [CodeSandbox](https://codesandbox.io/s/goofy-morse-ql9kl7?file=/src/index.tsx)

## Working with objects

The previous example was a simple one, now let's try to work with objects, which will be the most use cases in your application, for example, we can use the atom to handle current user data throughout the entire application life cycle.

```ts
// src/atoms/user-atom.ts
import { atom } from '@mongez/react-atom';

export type UserData = {
  id: number;
  name: string;
  email: string;  
};

export const userAtom = atom<UserData>({
  key: 'user', 
  default: {},
});
```

Here we defined a type for the data `UserData`, we passed it to the atom so we can work with proper typings when using the atom in the application,

Now let's head back again to our header and add the current user name

`src/components/Header.tsx`

```tsx
import { userAtom } from 'src/atoms/user-atom';

export default function Header() {
  // get the current value of the atom
  // and watch for any changes for the atom value
  // if the atom's value is changed, re-render the component with the new value
  const currentUser = userAtom.useValue();
  const userName = currentUser.name;
  return (
    <>
      <div>{userName}</div>
    </>
  );
}
```

Here we are listening to the atom's change, once the atom's value is updated then  the component will be re-rendered with the new value.

Now let's add an input to change the user name in the footer

`src/components/Footer.tsx`

```tsx
import { userAtom } from 'src/atoms/user-atom';

export default function Footer() {
  return (
    <>
        <input defaultValue={userAtom.get('name')} onChange={e => {
            userAtom.update({
              ...userAtom.value,
              name: e.target.value
            });
        }} />
    </>
  )
}
```

Here we are using `get` method to get the current value of the atom, then we are updating the atom's value with the new value, but we are not using `update` method, because it will replace the entire value, we are using `update` method to update the value with the new value, but we are keeping the old values.

> `get` method works only with objects, it will return the value of the given key from the object.

This will cause the header to be re-rendered with the new value.

We can use `change` method to change single key's value

`src/components/Footer.tsx`

```tsx
import { userAtom } from 'src/atoms/user-atom';

export default function Footer() {
  return (
    <>
        <input defaultValue={userAtom.get('name')} onChange={e => {
            userAtom.change('name', e.target.value);
        }} />
    </>
  )
}
```

> `change` method works only with objects, it will update the value of the given key from the object.

## Listen for certain changes

That was great so far, but what if the `email` is changed, the `Header` component has nothing to do with the email, it needs only the name thus it will cause unnecessary re-rendering when the atom's value is changed, thankfully we can listen for certain changes only using `use` hook.

`src/components/Header.tsx`

```tsx
import { userAtom } from 'src/atoms/user-atom';

export default function Header() {
  // get and listen for changes only for the name key
  const userName = userAtom.use('name');
  return (
    <>
      <div>{userName}</div>
    </>
  );
}
```

This will cause the header to be re-rendered only when the name is changed, but what if we want to listen for multiple keys, we can use `useMany` hook.

Even though the atom's object reference is changed, this will not cause any re-rendering, because the header is listening only for the name not the entire object.

## Atom update

As we saw earlier we can update the atom's value using `update` method, there is also another way to update the atom, same as the `state updater` by passing a callback function to the `update` method.

```tsx
import { userAtom } from 'src/atoms/user-atom';

userAtom.update((oldValue) => {
  return {
    ...oldValue,
    name: 'Ali',
  };
});
```

## Partial updates

When dealing with objects or arrays, To make the atom trigger the change event, we must pass a new reference to the atom's update method, same as `useState` hook, in that sense, we need to spread the old value along with the updated keys to make the atom trigger the change event.

```tsx
import { userAtom } from 'src/atoms/user-atom';

userAtom.update({
  ...userAtom.value,
  name: 'Ali',
});
```

This is the way to tell the atom the value has been changed, it is standard and easy to do, but we can use `merge` method as a shortcut to do the same thing.

```tsx
import { userAtom } from 'src/atoms/user-atom';

userAtom.merge({
  name: 'Ali',
});
```

> Kindly note that `merge` method works only with objects.

## Before update

Sometimes you may need to make a modification for the data before updating it in the atom or even do other stuff, for example, you may need to make a request to the server to update the data, then you can use `beforeUpdate` method to do that.

```tsx
// src/atoms/user-atom.ts
import { atom } from '@mongez/react-atom';

export type UserData = {
  id: number;
  name: string;
  email: string;
  initial: string;
};

export const userAtom = atom<UserData>({
  key: 'user',
  default: {},
  beforeUpdate: (newValue: UserData) => {
    newValue.initial = newValue.name[0];

    return newValue;
  }
});

// src/components/Header.tsx

import { userAtom } from 'src/atoms/user-atom';

userAtom.update({
  ...userAtom.value,
  name: 'Ali',
});

console.log(userAtom.get('initial')); // A
```

Here we used `beforeUpdate` to modify the value before storing it in the atom.

## Conclusion

We can recap the atom's methods as follows:

- `update` to update the atom's value
- `merge` to update the atom's value with partial updates
- `use` to listen for changes for a certain key
- `useValue` to listen for changes for the entire atom's value
- `get` to get the value key of the atom
- `change` to change the value of a key in the atom
- `beforeUpdate` to modify the value before updating it in the atom

You can find the documentation for package in [Github](https://github.com/hassanzohdy/mongez-react-atom)

## 😍 Join our community

Join our community on [Discord](https://discord.gg/pb2vmdfhGf) to get help and support (Node Js 2023 Channel).

## 📚 Bonus Content 📚

You may have a look at these articles, it will definitely boost your knowledge and productivity.

General Topics

- [Event Driven Architecture: A Practical Guide in Javascript](https://dev.to/hassanzohdy/event-driven-architecture-the-best-paradigm-that-i-love-to-work-with-in-javascript-and-node-js-1gnk)
- [Best Practices For Case Styles: Camel, Pascal, Snake, and Kebab Case In Node And Javascript](https://dev.to/hassanzohdy/best-practices-for-case-styles-camel-pascal-snake-and-kebab-case-in-node-and-javascript-55oi)
- [After 6 years of practicing MongoDB, Here are my thoughts on MongoDB vs MySQL
](https://dev.to/hassanzohdy/after-6-years-of-practicing-mongodb-here-are-my-thoughts-on-mongodb-vs-mysql-574b)

Packages & Libraries

- [Collections: Your ultimate Javascript Arrays Manager](https://dev.to/hassanzohdy/collections-your-ultimate-javascript-array-handler-3o15)
- [Supportive Is: an elegant utility to check types of values in JavaScript](https://dev.to/hassanzohdy/supportive-is-an-elegant-utility-to-check-types-of-values-in-javascript-1b3e)
- [Localization: An agnostic i18n package to manage localization in your project](https://dev.to/hassanzohdy/mongez-localization-the-simplest-way-to-translate-your-website-regardless-your-favorite-framework-4gi3)

Courses (Articles)

- [OOP In JS And TS From The Very Beginning](https://dev.to/hassanzohdy/jsts-oop-course-1-introduction-2ld)
- [ES6++: The Ultimate Guide to ES6 and Beyond](https://dev.to/hassanzohdy/es6-1-introduction-h3e)
- [React Js: Let"s Create File Manager With React Js and Node Js](https://dev.to/hassanzohdy/lets-create-a-file-manager-from-scratch-with-react-and-typescript-chapter-i-a-good-way-to-expand-your-experience-5g4k)