React JS와 함께 Zusstand 사용하기! 🚀

상태 관리는 최신 React JS 애플리케이션에서 필수입니다. 그렇기 때문에 오늘 저는 귀하의 지원서에서 귀하의 상태를 관리할 수 있는 인기 있는 대안인 "Zusstand"를 소개하겠습니다.

어떤 종류의 피드백이든 환영합니다. 감사합니다. 기사를 즐기시기 바랍니다.🤗

🚨 Note: This post requires you to know the basics of React with TypeScript.



목차.



📌 What is Zustand?

📌 Advantages of using Zustand.



📌 Creating the project.
📌 Creating a store.
📌 Accessing the store.

📌 Accessing multiple states.

📌 Why do we use the shallow function?



📌 Updating the state.

📌 Creating an action.

📌 Accessing the state stored in the store.

📌 Executing the action.



📌 Conclusion.

🚀 Zusstand란?

Zustand is a small, fast and scalable status management solution. Its state management is centralized and action-based.

Zustand was developed by Jotai and React-spring's creators.

You can use Zustand in both React and some other technology like Angular, Vue JS or even vanilla JavaScript.

Zustand is an alternative to other state managers like Redux, Jotai Recoil, etc.

⭕ Zusstand 사용의 장점.

  • Less repeated code (compared to Redux).
  • Easy to understand documentation.
  • Flexibility
    • You can use Zustand the simple way, with TypeScript, you can integrate immer for immutability or you can even write code similar to the Redux pattern (reducers and dispatch).
  • It does not wrap the application in a provider as is commonly done in Redux.
  • Renders components only when there are changes.

🚀 프로젝트 생성.

We will name the project: zustand-tutorial (optional, you can name it whatever you like).

npm init vite@latest

We create the project with Vite JS and select React with TypeScript.

Then we run the following command to navigate to the directory just created.

cd zustand-tutorial

Then we install the dependencies.

npm install

Then we open the project in a code editor (in my case VS code).

code .

🚀 매장 만들기.

First we must install Zustand:

npm install zustand

Once the library is installed, we need to create a folder src/store and inside the folder we add a new file called bookStore.ts and inside this file, we will create our store.

First we import the zustand package and name it bookStore.ts . create

import create from 'zustand';

Then we create a constant with the name useBookStore (this is because zustand uses hooks underneath and in its documentation it names the stores this way).

To define the store we use the create function.

import create from 'zustand';

export const useBookStore = create();

The create function takes a callback function as a parameter, which returns an object, to create the store.

import create from 'zustand';

export const useBookStore = create( () => ({

}));

For better autocompletion, we will use an interface to define the properties of our store, as well as the functions.

Then we set the initial value of the properties, in this case the amount property will initially be 40.

import create from 'zustand';

interface IBook {
    amount: number 
}

export const useBookStore = create<IBook>( () => ({
    amount: 40 
}));

🚀 매장접속.

To access our store, we need to import our store.
In our src/App.tsx file we import our store.

Without using providers like in Redux, we can use our store almost anywhere ("almost" because it follows the rules of hooks, since the store is basically a hook underneath).

Basically we call to our hook, like any other, only that by parameter we must indicate it by means of a callback that property we want to obtain of the store and thanks to the autocomplete it helps us a lot.

import { useBookStore } from './store/bookStore';
const App = () => {

  const amount = useBookStore(state => state.amount)

  return (
    <div>
      <h1>Books: {amount} </h1>
    </div>
  )
}
export default App

⭕ 여러 상태에 액세스.

Suppose you have more than one state in your store, for example, let's add the title:

import create from 'zustand';

interface IBook {
    amount: number
    author: string
}

export const useBookStore = create<IBook>( () => ({
    amount: 40,
    title: "Alice's Adventures in Wonderland"
}));

To access more states we could do the following:

Case 1 - One way is individually, go accessing the state, creating new constants.

import { useBookStore } from './store/bookStore';
const App = () => {

  const amount = useBookStore(state => state.amount)
  const title = useBookStore(state => state.title)

  return (
    <div>
      <h1>Books: {amount} </h1>
    </div>
  )
}
export default App

Case 2 - But if you want, you can also create a single object with multiple states or properties. And to tell Zustand to diffuse the object shallowly, we must pass the shallow function.

import shallow from 'zustand/shallow'
import { useBookStore } from './store/bookStore';

const App = () => {

  const { amount, title } = useBookStore(
    (state) => ({ amount: state.amount, title: state.title }),
    shallow
  )

  return (
    <div>
      <h1>Books: {amount} </h1>
      <h4>Title: {title} </h4>
    </div>
  )
}
export default App

Although it would be better to place the store in a separate hook if it grows too much in terms of properties.

In both case 1 and case 2 the components will be rendered when the title and amount change.

🔴 얕은 기능을 사용하는 이유는 무엇입니까?

In the above case where we access several states of the store, we use the shallow function, why?

By default if we do not use shallow, Zustand detects changes with strict equality (old === new), which is efficient for atomic states.

 const amount = useBookStore(state => state.amount)

But in case 2, we are not obtaining an atomic state, but an object (the same happens if we use an array).

  const { amount, title } = useBookStore(
    (state) => ({ amount: state.amount, title: state.title }),
    shallow
  )

So the default strict equality would not be useful in this case to evaluate objects and always triggering a re-render even if the object does not change.

So Shallow will upload the object/array and compare its keys, if any is different it will recreate again and trigger a new render.

🚀 상태 업데이트 중.

To update the state in the store we must do it by creating new properties in src/store/bookStore.ts adding functions to update modify the store.

In the callback that receives the create function, this function receives several parameters, the first one is the set function, which will allow us to update the store.

export const useBookStore = create<IBook>(( set ) => ({
    amount: 40
}));

⭕ 액션 만들기.

First we create a new property to update the amount and it will be called updateAmount which receives a number as parameter.

import create from 'zustand'

interface IBook {
    amount: number
    updateAmount: (newAmount: number) => void
}

export const useBookStore = create<IBook>((set) => ({
    amount: 40,
    updateAmount: (newAmount: number ) => {}
}));

In the body of the updateAmount function we execute the set function by sending an object, referencing the property to be updated.

import create from 'zustand'

interface IBook {
    amount: number
    updateAmount: (newAmount: number) => void
}

export const useBookStore = create<IBook>( (set) => ({
    amount: 40,
    updateAmount: (newAmount: number ) => set({ amount: newAmount }),
}));

The set function can also receive a function as a parameter, which is useful to get the previous state.

Optionally I scatter the whole state (assuming I have more properties) and only update the state I need, in this case the amount.

Note: Spreading properties should also be taken into account when your states are objects or arrays that are constantly changing.

updateAmount: (newAmount: number ) => set( state => ({ ...state, amount: state.amount + newAmount }))

You can also do asynchronous actions as follows and that's it!

updateAmount: async(newAmount: number ) => {
    // to do fetching data
    set({ amount: newAmount })
}

💡 Note: the set function accepts a second boolean parameter, default is false. Instead of merging, it will replace the state model. You must be careful not to delete important parts of your store such as actions.

  updateAmount: () => set({}, true), // clears the entire store, actions included,

⭕ 스토어에 저장된 상태에 접근하기.

To define the state we use the set function, but what if we want to get the values of the state?

Well for that we have the second parameter next to set, which is get() that gives us access to the state.

import create from 'zustand'

interface IBook {
    amount: number
    updateAmount: (newAmount: number) => void
}

export const useBookStore = create<IBook>( (set, get) => ({
    amount: 40,
    updateAmount: (newAmount: number ) => {

        const amountState = get().amount

        set({ amount: newAmount + amountState })
        //is the same as:
        // set(state => ({ amount: newAmount + state.amount  }))
    },
}));

⭕ 작업 실행.

To execute the action, it is simply to access the property as we have done previously. And we execute it, sending the necessary parameters, that in this case is only a number.

import { useBookStore } from './store/bookStore';
const App = () => {

  const amount = useBookStore(state => state.amount)
  const updateAmount = useBookStore(state => state.updateAmount)

  return (
    <div>

      <h1> Books: {amount} </h1>

      <button 
        onClick={ () => updateAmount(10) } 
      > Update Amount </button>

    </div>
  )
}
export default App

🚀결론.

Zustand provides easy access and update of status, which makes it a friendly alternative to other status managers.

In my personal opinion, Zustand has pleased me a lot for its above mentioned features, it is one of my favorite libraries to manage status, as well as Redux Toolkit. You should definitely give it a try to use it in some project 😉.

I hope I have helped you to better understand how this library works and how to use it, thank you very much for coming this far! 🤗

I invite you to comment if you know of any other important features of Zustand or best practices for code. 🙌

좋은 웹페이지 즐겨찾기