Frontend Development 30 min read

Unlocking Progressive State Management with Zustand – Is There a Silver Bullet?

This article walks through using the Zustand state‑management library in a complex React component, covering store initialization, actions, selectors, modular file structure, handling controlled vs uncontrolled modes, performance optimizations, and devtools integration, while sharing practical tips and code examples for scalable frontend development.

Alipay Experience Technology
Alipay Experience Technology
Alipay Experience Technology
Unlocking Progressive State Management with Zustand – Is There a Silver Bullet?

Is There a Silver Bullet?

In the previous article the author introduced the motivations for using Zustand in the ProEditor scenario. This piece details how to solve all state‑management requirements with Zustand from a practical perspective.

There is no silver bullet. Choose a remote‑state library if it meets your needs; otherwise use useState + Context for simple cases, or consider external stores like Valtio or Zustand for more complex scenarios.

The core challenge is whether the current approach can handle more complex situations, such as expanding from 5 to 15 states, integrating a canvas chart, or extracting a component for external reuse.

Developer experience (DX) of the library.

Ability to accommodate future complex scenarios with low cost.

Progressive State Management with Zustand

The author uses an icon selector component as a real‑world example. The component must display, select, delete, search icons, switch between Ant Design and Iconfont sources, and add or remove Iconfont scripts.

Step 1: Store Initialization – State

Create

store.ts

and define the initial state:

<code>import create from 'zustand';
// useStore is the hook
export const useStore = create(() => ({
  panelTabKey: 'antd',
  // other state fields …
}));</code>

In React components import

useStore

to read

panelTabKey

and use

useStore.setState

to modify it.

Step 2: Actions – State‑Changing Methods

Define custom actions such as

selectIcon

that update multiple pieces of state and can be reused across components.

<code>export const useStore = create(set => ({
  // …
  selectIcon: icon => {
    set({ icon, open: false, filterKeywords: undefined });
  },
}));</code>

Actions support async/await and are memoized by Zustand, avoiding unnecessary re‑renders.

Step 3: Selector – Derived State

Complex derived state like

iconList

is implemented with selectors that filter based on the active tab and search keywords.

<code>export const displayListSelector = s => {
  const list = s.panelTabKey === 'iconfont' ? s.iconfontIconList : s.antdIconList;
  const { filterKeywords } = s;
  return list.filter(i => {
    if (!filterKeywords) return true;
    switch (i.type) {
      case 'antd':
      case 'internal':
        return i.componentName.toLowerCase().includes(filterKeywords.toLowerCase());
      case 'iconfont':
        return i.props.type.toLowerCase().includes(filterKeywords);
    }
  });
};</code>

Step 4: Organizing Files and Types

Split the store into three files:

initialState.ts

– State type and initial values.

createStore.ts

– Store creation and actions.

selectors.ts

– Selector functions.

Export them from

index.ts

for easy consumption.

Step 5: Complex Actions with get()

Use

get()

inside actions to read the whole store, enabling multi‑step operations such as adding a script, hiding the form, updating the script list, and selecting the new script.

Step 6: From Application to Component – Context and StoreUpdater

Wrap the store with

createContext

to create a provider, allowing multiple isolated instances. Implement a

StoreUpdater

component that syncs external props (e.g.,

icon

,

iconfontScripts

) to the internal store, supporting controlled usage.

Step 7: Performance Optimization – Selectors

Apply shallow‑compare selectors to subscribe only to the needed slices of state, preventing re‑renders when unrelated fields change.

<code>import shallow from 'zustand/shallow';
const selector = s => ({ panelTabKey: s.panelTabKey, icon: s.icon, resetIcon: s.resetIcon });
const { panelTabKey, icon, resetIcon } = useStore(selector, shallow);
</code>

Step 8: DevTools Integration

Wrap the store with

devtools

middleware and provide descriptive action names for better debugging.

<code>export const createStore = () =>
  create(devtools((set, get) => ({
    // state and actions …
  }), { name: 'IconPicker' }));
</code>

Optionally pass a third argument to

set

for a custom label, e.g.,

set({ icon }, false, 'Select Icon')

.

Additional notes mention possible extensions such as integrating Redux reducers, RxJS, SWR, persistence, history (zundo), subscriptions, slice‑based stores, and third‑party libraries like Y‑js.

Zustand workflow
Zustand workflow
performance optimizationfrontend developmentState ManagementreactZustandControlled Components
Alipay Experience Technology
Written by

Alipay Experience Technology

Exploring ultimate user experience and best engineering practices

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.