Jotai Atoms Are Just Functions

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

Rate this content
Bookmark
Slides
Video Summary and Transcription
Jotai is a state management library for React that introduces a concept based on atoms, which are essentially functions that define pieces of state. Unlike traditional state management libraries, Jotai's atoms form a dependency graph to propagate updates. These atoms are similar to observables but have distinct features. Atoms in Jotai can be used to define both global and local states, making them versatile in managing different scopes of state. To access the value of an atom without using a hook, developers can utilize the get function. Jotai also supports dynamic atoms, allowing for complex dependency graphs where atom dependencies can be conditional or based on multiple atoms. The useAtom hook in Jotai is akin to useState in React, offering a familiar experience for developers. Furthermore, Jotai's framework-agnostic nature means it can be used outside of React, with experimental libraries like Jotajsx exploring its potential. Primitive atoms in Jotai are defined with an initial value and act as the data source in the dependency graph, providing a flexible and efficient way to manage state in React applications.

This talk has been presented at React Day Berlin 2022, check out the latest edition of this React Conference.

FAQ

Jotai is a state management library for React that utilizes a concept based on atoms, which represent pieces of state. It enables developers to create and manage state in a React application by forming a dependency graph of these atoms to propagate updates. Atoms in Jotai are like functions that can depend on other atoms and are used with the useAtom hook similar to useState in React.

The main features of Jotai include its ability to define state as atoms, which are functions that can have dependencies on other atoms, creating a dynamic dependency graph. Jotai allows for both read-only and writable atoms, supports complex state relationships, and integrates seamlessly with React through the useAtom hook and ProviderComponent.

Yes, Jotai can be used for both local and global state management in React applications. While it is often considered a global state solution, it can also be used for managing semi-global or local states depending on the implementation and structure of the atoms.

Jotai differs from other state management libraries by focusing on a minimalistic approach using atoms, which are small units of state. Unlike traditional state management solutions that might use reducers or context, Jotai's atoms are designed to be more granular and composable, allowing for more flexible and efficient state updates.

In Jotai, an atom represents a piece of state. Atoms can depend on other atoms, forming a dependency graph. They do not hold values themselves but are definitions where the values are stored externally. Atoms are used within React components using the useAtom hook, which provides the current state value and a function to update it.

While Jotai is primarily designed for React, the fundamental concept of atoms as state definitions is framework-agnostic. The creator of Jotai has experimented with libraries like Jotajsx to explore the use of Jotai's state management approach outside of React, showing potential for broader applications.

1. Introduction to Jotai Library#

Short description:

State management in React is a highly discussed topic with many libraries and solutions. Jotai is a new library based on atoms, which represent pieces of state. It forms a dependency graph to propagate updates. The concept is similar to observables for async data flow, but atoms have some differences.

Hello, everyone. Thanks for an opportunity to give this talk. I hope you find it useful.

As many of you may know, state management in React is one of the most discussed topics in the community. There are many libraries and solutions. UseStateHook is one of the primitive solutions. Some of the popular libraries include Redux, MoveX, XState, and Zustand. They provide different functionalities for different goals. The good thing is that developers have many options to develop their apps. The bad thing is that there are too many options. But I think having many options is still good for ecosystem. If there were only one solution, we would miss many new ideas.

Jotai is a new library in this field. Hi, my name is Daishi Kato. I'm author of Jotai Library. I am half open source developer and half freelancer. My open source software is with JavaScript and React. And my work is also related with JavaScript and React. There are quite a few open source projects that I'm working on, including experimental ones. Jotai is one of my open source projects, but we develop it as a team. While I'm the main developer of the code, there are many contributors, not only for coding, but also for documentation and other stuff.

This talk is about Jotai library, which is one solution for state management in React. Jotai is a library based on atoms, which represent pieces of state. Atoms are popularized by a library called Recoil, but the concept is not very new. The concept is basically to form a dependency graph of pieces of state and propagate updates. For example, suppose we have three atoms, A, B and C. A depends on B, B depends on C. If we update C, both B and A are updated. This pattern is already done, for example, with observables for async data flow. Atoms are a little different from observables.

2. Atoms and State Management in Jyotai Library#

Short description:

Usually, atoms in Jyotai are used to define state without holding values. They are like functions that depend on other atoms. Changing an atom triggers updates in other dependent atoms. UseAtom hook is similar to useState and returns a value and an update function. Atoms can be used for global, semi-global, or local states.

Usually, an observable object would hold a value or maybe it's initially empty. Atoms would never hold values. They are just definitions and values exist somewhere else. We will get into it in this talk, but let's first see how the usage of Jyotai looks like.

This is a simple example using Jyotai atoms. We have three atoms, textAtom, textLengthAtom, and uppercaseAtom. textAtom has initial value HELLO. textLengthAtom has a function returning the length of textAtom. uppercaseAtom has a function similarly returning the upper case string of textAtom. stringAtom and uppercaseAtom both depend on textAtom. So if you change textAtom, the other two atoms will also be changed.

As you see, if we either text, I mean if we enter a text in the text field, all three values are updated accordingly. If you look closely, we use useAtom hook which takes an atom we defined. UseAtom hook works like useState hook. It returns a value and an update function. If the value is changed, it will trigger a re-render. You can change the value with the update function.

There is one important note which isn't shown in this example. If we use the same atom, useAtom returns the same value. So we can use atoms for global state. Jyotai library is often considered as a global state solution. We can use it for global state, but it's not truly global. And we can use it for semi-global or local states. This may sound unintuitive, but if you think atoms as functions, it should make more sense. Let's try to make an analogy. We all know React components are just functions. This is one of the simplest components. It returns a string. We usually define components that return JSX elements, but returning strings is also valid. We don't exactly know when this function is invoked.

3. Atoms and Dependencies in Jotai Library#

Short description:

React invokes functions declaratively. Components and atoms are both functions. Atoms are used to define static values. To add a dependency, we can use the context pattern. Components can declare dependencies, but invocation is not determined.

React will invoke this function whenever necessary. It's declarative. Components can be said to be declarative functions. Let's move on. Components are functions. Likewise, atoms are functions.

This is a similar example and it's a valid atom definition. We have a wrapping function named atom, but it's not very important. It's just a helpful function to build an AtomConfig object, which helps especially in TypeScript. In this case, it will create an object with the read property, which contains the function. The function is exactly the same as what we saw in the text component in the previous slide. This is an atom whose value is the return value of the function, which is a string, hello. It's so to say a static atom whose value never changes.

A static atom is not very useful, so how do we add a dependency? Let's see how a component looks like. With React components, there are roughly three patterns to define dependencies, props, states and context. Let's take context as an example, because it's good for analogy. In this example, we create count context and define double component. Double component will use the context value, double it, and return it. Whenever the context value is changed, we expect double component will return an updated value. But we don't know when the component function will be invoked, because it's declarative. What we declare is that double component depends on count context. How the function is invoked is not determined at this point. Are you OK with this example?

4. Atom Definitions and Dependencies#

Short description:

We define count atom and double atom. The get function will return the value of an atom. Double atom depends on count atom. When we define atoms, we don't know what are atom values or even where atom values are stored. We can change atom dependencies. QuadrupleAtom depends on doubleAtom, and doubleAtom depends on CountAtom. Some more patterns are possible. For example, single atom can depend on multiple atoms. Another example is atom dependency can be conditional. Using those patterns, we can freely define atoms and make a complex dependency graph. We basically covered how to define atoms in terms of reading their values. Let's see how we can update the values.

We define count atom and double atom. The definition of the count atom is a special syntax which corresponds to create context in the previous example. We will learn about this syntax a little bit later. So far, it's an atom definition that we can use from other atoms.

The following is double atom definition. Notice a function named get. We don't define it. It will be injected from somewhere. The get function will return the value of an atom. It's like use context returns the context value. Double atom depends on count atom. And when count atom changes, double atom should be re-evaluated.

When we define atoms, we don't know what are atom values or even where atom values are stored. What we know is when the function is invoked, it will receive the get function, and using the get function, you can get the value of the atom. This is pretty similar to how use context hook works. The difference is that in atoms, the get function is injected from the function parameter, whereas use context is exported from the react library.

There's another difference. We can change atom dependencies. We define a new atom called quadrupleAtom. The internal function returns double value of doubleAtom, which is the quadruple value of the original CountAtom. QuadrupleAtom depends on doubleAtom, and doubleAtom depends on CountAtom. So the dependencies are chained. In practice, it will form a tree or even graph of dependencies. Now we learned the basic pattern of how atoms form a simple dependency graph. Some more patterns are possible. For example, single atom can depend on multiple atoms. Another example is atom dependency can be conditional. Using those patterns, we can freely define atoms and make a complex dependency graph. We basically covered how to define atoms in terms of reading their values. Let's see how we can update the values.

5. Updating Atom Values#

Short description:

To update the values in Jotai, we can change the definition of an atom by adding a second parameter. The first parameter is the read function, and the second parameter is the write function. The write function has three parameters: get, set, and new value. We can define these functions freely, allowing for write-only atoms. However, this topic is beyond the scope of this talk.

Let's see how we can update the values. We changed the definition of double atom by adding a second parameter. The first parameter is called read function, and the second parameter is called write function. Those functions are just put in the read and write properties of the resulting atom object. The write function has three parameters, get, set, and new value. In this case, it divides a new value by 2 and sets it to the original count atom. Providing the write function makes the atom writable. The read and write functions of double atoms are symmetric, but they don't have to be symmetric. We can define those functions freely. This opens up a pattern called write-only atoms, but it's out of the scope of this talk.

6. Definition of Count Atom and Primitive Atoms#

Short description:

The final missing piece is the definition of count atom. Primitive atoms are defined by the atom function with a single initial value. They are the sources of data in the dependency graph.

Now, the final missing piece is the definition of count atom at the first line. We call such atoms primitive atoms. From the perspective of the dependency graph, they are the sources of data.

Primitive atoms are defined by the atom function with a single initial value. In this case of this count atom, zero is the initial value. This is a special syntax to create a writable atom with an initial value. The resulting atom config object has three properties, Init, Read, and Write. The Initi property has the initial value. The Read and Write functions do simply read from and write to the self-atom.

We have learned a normal read-only atom, a writable atom, and a primitive atom. Read-only atoms are atoms that compute from other atoms. Writable atoms are read-only atoms plus write-back capability. Primitive atoms are special writable atoms that can write to themselves. They form a dependency graph and primitive atoms are the source of the graph.

7. Using Atoms with React#

Short description:

Atoms are pure definitions of state that depend only on JavaScript. To use atoms in React, we need ProviderComponent and UseAtomHook. ProviderComponent stores atom values, while UseAtomHook retrieves the current value and update function. Learning UseAtom is easy for developers familiar with UseState. The code example shows how atom values change and can be seen on the screen. In some cases, Provider can be omitted, using global state for atom values. However, multiple Providers can be used in other cases.

At the beginning of this talk, I introduced Jotai as a state management solution for React. As we saw how to build atoms, they have nothing to do with React. Atoms are pure definitions of state that depends only on JavaScript. The atom function may seem like magic, but it's just a helper function to make a config object. Technically, you could define the atom object without using the helper function.

So if atoms are not things of React, how can we use them with React? To use atoms in React we need two functions. One is ProviderComponent. The other is UseAtomHook. As I keep saying, atom objects are just definitions and they don't hold values. So we need a place to store atom values. That's the ProviderComponent. It has values of all atoms under the component tree. To use an atom value in the ProviderComponent, we use UseAtomHook in Child component. It takes an atom object and returns the current value in the ProviderComponent and the update function to change the value in Provider. UseAtom's usage is similar to UseState and UseReducer. So learning UseAtom is almost nothing for developers who are already familiar with UseState.

Let's look at the same example we looked earlier again. TextAtom is a primitive atom, and the other two atoms are ReadOnly atoms. They depend on TextAtoms. So when TextAtom value changes, all three atom values change, and updates can be seen on screen. The code in the slide doesn't show the Provider component, but it's there at the bottom. However, in some cases, we can actually omit Provider. For many use cases, we would have one Provider close to the root of component tree. If it is just one Provider in the memory, we can omit it. It then uses a global state for atom values. It's a very handy feature and often used. If we never have a Provider component, theoretically atoms can hold values. So what's the point of separating atoms and values? I just said if there is one Provider in the memory, we can omit it. That means for other cases, having multiple Providers is meaningful and it's possible if atoms don't hold values. This is an example with two Providers.

8. Atoms in Jotai Library and Experimental Libraries#

Short description:

Atoms in Jotai are reusable definitions that are independent from React. They can be used without React in an experimental library called Jotajsx. JotaiSignal and JotaiUncontrolled are two other experimental libraries. Jotai atoms are framework agnostic state definitions. For existing Jotai users, keep in mind that atoms are just like functions. New users are encouraged to try the Jotai library.

Notice we have one atom defined, which is CountAtom. The Counter component is also defined once. Now we use the Counter component in two Providers. Because those two Providers have different atom values, we see two different values on screen. Let's see how it works. The Counts are separated. Clicking both ends affects only the Count in one Provider. This is what I mean. Atom definition is reusable, it's just like a function.

There's another case where atoms shouldn't hold values. In server context, there are multiple requests, and each request starts a new render. We need a Provider component for each render to isolate states between requests. Atom definitions can be shared between requests, but atom values are not. This is actually hypothetical because at the moment there's basically no way to update atom values on server. That being said, it feels nice to separate definition and values if we handle multiple things in a single memory space. And we could do more things on server in the future.

So if atoms are just definitions and independent from React, can we use them without React? To answer that question, I made an experimental library called Jotajsx. It's a library that technically replaces React and React-dom libraries, as the syntax is exactly the same as normal Jotai usage with React. We can run a Jotai app with only changing the import lines. It is not feature-complete and may contain bugs, but it shows a proof of concept. Other two experimental libraries are called JotaiSignal and JotaiUncontrolled. They basically work with React, but the syntax is different. The one is a signal style and the other is for uncontrolled components. Their implementations come with hacks, but they should also show the proof of Atom concept. In the future, the Jotai library will eventually expose vanilla functions that are not tied to React. And we would be able to build more libraries in our ecosystem. These three libraries are hosted under the GitHub organization named jotailabs. There we hope to improve libraries for practical use and add more libraries for experiments.

In summary, the takeaway is the following. Jotai atoms are just like functions. And they are framework agnostic state definitions. If you are already Jotai users, please try to keep that in mind which may help designing atoms. If you are new to Jotai library, please give it a try. We have a website at jotai.org and there are some documentations. We look forward to communicating with users and contributors. This is the end of my talk.

Daishi Kato
Daishi Kato
22 min
05 Dec, 2022

Comments

Sign in or register to post your comment.

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

Don't Solve Problems, Eliminate Them
React Advanced 2021React Advanced 2021
39 min
Don't Solve Problems, Eliminate Them
Top Content
Kent C. Dodds discusses the concept of problem elimination rather than just problem-solving. He introduces the idea of a problem tree and the importance of avoiding creating solutions prematurely. Kent uses examples like Tesla's electric engine and Remix framework to illustrate the benefits of problem elimination. He emphasizes the value of trade-offs and taking the easier path, as well as the need to constantly re-evaluate and change approaches to eliminate problems.
Everything Beyond State Management in Stores with Pinia
Vue.js London Live 2021Vue.js London Live 2021
34 min
Everything Beyond State Management in Stores with Pinia
Top Content
State management is not limited to complex applications and transitioning to a store offers significant benefits. Pinia is a centralized state management solution compatible with Vue 2 and Vue 3, providing advanced devtools support and extensibility with plugins. The core API of Pinia is similar to Vuex, but with a less verbose version of stores and powerful plugins. Pinia allows for easy state inspection, error handling, and testing. It is recommended to create one file per store for better organization and Pinia offers a more efficient performance compared to V-rex.
React Compiler - Understanding Idiomatic React (React Forget)
React Advanced 2023React Advanced 2023
33 min
React Compiler - Understanding Idiomatic React (React Forget)
Top Content
Watch video: React Compiler - Understanding Idiomatic React (React Forget)
Joe Savona
Mofei Zhang
2 authors
The Talk discusses React Forget, a compiler built at Meta that aims to optimize client-side React development. It explores the use of memoization to improve performance and the vision of Forget to automatically determine dependencies at build time. Forget is named with an F-word pun and has the potential to optimize server builds and enable dead code elimination. The team plans to make Forget open-source and is focused on ensuring its quality before release.
Using useEffect Effectively
React Advanced 2022React Advanced 2022
30 min
Using useEffect Effectively
Top Content
Today's Talk explores the use of the useEffect hook in React development, covering topics such as fetching data, handling race conditions and cleanup, and optimizing performance. It also discusses the correct use of useEffect in React 18, the distinction between Activity Effects and Action Effects, and the potential misuse of useEffect. The Talk highlights the benefits of using useQuery or SWR for data fetching, the problems with using useEffect for initializing global singletons, and the use of state machines for handling effects. The speaker also recommends exploring the beta React docs and using tools like the stately.ai editor for visualizing state machines.
Speeding Up Your React App With Less JavaScript
React Summit 2023React Summit 2023
32 min
Speeding Up Your React App With Less JavaScript
Top Content
Watch video: Speeding Up Your React App With Less JavaScript
Mishko, the creator of Angular and AngularJS, discusses the challenges of website performance and JavaScript hydration. He explains the differences between client-side and server-side rendering and introduces Quik as a solution for efficient component hydration. Mishko demonstrates examples of state management and intercommunication using Quik. He highlights the performance benefits of using Quik with React and emphasizes the importance of reducing JavaScript size for better performance. Finally, he mentions the use of QUIC in both MPA and SPA applications for improved startup performance.
React Query: It’s Time to Break up with your "Global State”!
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
Global state management and the challenges of placing server state in global state are discussed. React Query is introduced as a solution for handling asynchronous server state. The Talk demonstrates the process of extracting logic into custom hooks and fixing issues with state and fetching logic. Optimistic updates with mutation are showcased, along with the benefits of using React Query for data fetching and mutations. The future of global state management is discussed, along with user feedback on React Query. The Talk concludes with an invitation to explore React Query for server state management.

Workshops on related topic

React, TypeScript, and TDD
React Advanced 2021React Advanced 2021
174 min
React, TypeScript, and TDD
Top Content
Featured WorkshopFree
Paul Everitt
Paul Everitt
ReactJS is wildly popular and thus wildly supported. TypeScript is increasingly popular, and thus increasingly supported.

The two together? Not as much. Given that they both change quickly, it's hard to find accurate learning materials.

React+TypeScript, with JetBrains IDEs? That three-part combination is the topic of this series. We'll show a little about a lot. Meaning, the key steps to getting productive, in the IDE, for React projects using TypeScript. Along the way we'll show test-driven development and emphasize tips-and-tricks in the IDE.
Web3 Workshop - Building Your First Dapp
React Advanced 2021React Advanced 2021
145 min
Web3 Workshop - Building Your First Dapp
Top Content
Featured WorkshopFree
Nader Dabit
Nader Dabit
In this workshop, you'll learn how to build your first full stack dapp on the Ethereum blockchain, reading and writing data to the network, and connecting a front end application to the contract you've deployed. By the end of the workshop, you'll understand how to set up a full stack development environment, run a local node, and interact with any smart contract using React, HardHat, and Ethers.js.
Remix Fundamentals
React Summit 2022React Summit 2022
136 min
Remix Fundamentals
Top Content
Featured WorkshopFree
Kent C. Dodds
Kent C. Dodds
Building modern web applications is riddled with complexity And that's only if you bother to deal with the problems
Tired of wiring up onSubmit to backend APIs and making sure your client-side cache stays up-to-date? Wouldn't it be cool to be able to use the global nature of CSS to your benefit, rather than find tools or conventions to avoid or work around it? And how would you like nested layouts with intelligent and performance optimized data management that just works™?
Remix solves some of these problems, and completely eliminates the rest. You don't even have to think about server cache management or global CSS namespace clashes. It's not that Remix has APIs to avoid these problems, they simply don't exist when you're using Remix. Oh, and you don't need that huge complex graphql client when you're using Remix. They've got you covered. Ready to build faster apps faster?
At the end of this workshop, you'll know how to:- Create Remix Routes- Style Remix applications- Load data in Remix loaders- Mutate data with forms and actions
Vue3: Modern Frontend App Development
Vue.js London Live 2021Vue.js London Live 2021
169 min
Vue3: Modern Frontend App Development
Top Content
Featured WorkshopFree
Mikhail Kuznetsov
Mikhail Kuznetsov
The Vue3 has been released in mid-2020. Besides many improvements and optimizations, the main feature of Vue3 brings is the Composition API – a new way to write and reuse reactive code. Let's learn more about how to use Composition API efficiently.

Besides core Vue3 features we'll explain examples of how to use popular libraries with Vue3.

Table of contents:
- Introduction to Vue3
- Composition API
- Core libraries
- Vue3 ecosystem

Prerequisites:
IDE of choice (Inellij or VSC) installed
Nodejs + NPM
Rethinking Server State with React Query
React Summit 2020React Summit 2020
96 min
Rethinking Server State with React Query
Top Content
Featured Workshop
Tanner Linsley
Tanner Linsley
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.
Developing Dynamic Blogs with SvelteKit & Storyblok: A Hands-on Workshop
JSNation 2023JSNation 2023
174 min
Developing Dynamic Blogs with SvelteKit & Storyblok: A Hands-on Workshop
Top Content
Featured WorkshopFree
Alba Silvente Fuentes
Roberto Butti
2 authors
This SvelteKit workshop explores the integration of 3rd party services, such as Storyblok, in a SvelteKit project. Participants will learn how to create a SvelteKit project, leverage Svelte components, and connect to external APIs. The workshop covers important concepts including SSR, CSR, static site generation, and deploying the application using adapters. By the end of the workshop, attendees will have a solid understanding of building SvelteKit applications with API integrations and be prepared for deployment.