1.0.25 β€’ Published 11 months ago

react-use-zustand v1.0.25

Weekly downloads
-
License
ISC
Repository
github
Last release
11 months ago

πŸ’Ύ react-use-zustand

version stars forks last commit code size minzip size download

Description - πŸ”Ž Wrapper over package zustand. Shortens the code, provides hook that returns setter and getter, convenient to work with typescript.

πŸ’Ώ Installation

npm i react-use-zustand

πŸ’» Example

base use

By default, the hook will return us an object with three fields: value, setter, and cleanup. The set method can be passed either a value or a callback. The clear method will reset the values to the values you defined in the "default" object. If you don't define a default value, it will return an undefined value. Data from the storage will also be deleted if you saved it there.

// store
import useZustand, { StoreTypes } from 'react-use-zustand';

interface Store {
  counter: number;
  tree: {
    node: {
      counter: number;
    };
  };
}

const baseStore = useZustand<Store>({
  keys: ['counter', 'tree'],
  default: {
    counter: 0,
    tree: {
      node: {
        counter: 0,
      },
    },
  },
});

export type BaseStoreTypes = StoreTypes<typeof baseStore.use>;
export default baseStore;


//component
import baseStore from './stores/base';

function App() {
  const counter = baseStore.use.counter();
  const tree = baseStore.use.tree();
  
  const mutatePrimitive = () => {
    counter.set((prev) => ++prev);
  };

  const updatePrimitive = () => {
    counter.set(1000);
  };
  
  const mutateObject = () => {
    tree.set((prev) => { ++prev.node.counter });
  };

  const updateObject = () => {
    tree.set({ node: { counter: 100000 } });
  };

  return (
    <>
      <button onClick={updatePrimitive}>update primitive</button>
      <button onClick={mutatePrimitive}>mutate primitive</button>
      <div>{counter.value}</div>
      <button onClick={updateObject}>update object</button>
      <button onClick={mutateObject}>mutate object</button>
      <div>{tree.value.node.counter}</div>
    </>
  );
}

with methods

We can register methods in the store itself to avoid code duplication, for example if we change values from different places.

// store
import useZustand, { StoreTypes } from 'react-use-zustand';

interface User {
  id: number;
  name: string;
}

interface Store {
  users: Array<User>;
}

interface Methods {
  users: {
    add: (user: User) => void;
    getById: (id: number) => User | undefined | any;
    deleteById: (id: number) => void;
  };
}

const storeWithMethods = useZustand<Store, Methods>({
  keys: ['users'],
  default: {
    users: [],
  },
  methods: {
    users: (getState) => ({
      add: (user) => {
        const state = getState();
        state.users.set((prev) => prev.push(user));
      },
      getById: (id) => {
        const state = getState();
        return state.users.value.find((user) => user.id === id);
      },
      deleteById: (id) => {
        const state = getState();
        state.users.set((prev) => {
          const foundIndex = prev.findIndex((user) => user.id === id);
          foundIndex !== -1 && prev.splice(foundIndex, 1);
        });
      },
    }),
  },
});

export type StoreWithMethodsTypes = StoreTypes<typeof storeWithMethods.use>;
export default storeWithMethods;


//component
import storeWithMethods from './stores/withMethods';

function App() {
  const [id, setId] = useState('');

  const users = storeWithMethods.use.users();

  const foundUser = useMemo(() => {
    return users.getById(+id);
  }, [id, users.value.length]);

  const random = Math.floor(Math.random() * 10000);

  return (
    <div>
      <button onClick={() => users.add({ id: random, name: `user-${random}` })}>add</button>
      {users.value.length ? (
        <div>
          <span>search</span>___
          <input type="number" value={id} onChange={(e) => setId(e.target.value)} />
          {foundUser && <span>___{foundUser.name}</span>}
        </div>
      ) : null}
      <ul>
        {users.value.map((user) => (
          <li key={user.id} onClick={() => users.deleteById(user.id)}>
            <span>{user.name}</span>___<button>delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

with storage

Storing data in storage. If there is data in the store, the value specified in the β€œdefault” object will be ignored.

OptionDescriptionvalueDefault
allsave the entire storeboolean
keyssave individual fieldsstring[]
storagewhere to savelocalStorage or sessionStoragelocalStorage
storageNamekeystring
// store
import useZustand, { StoreTypes } from 'react-use-zustand';

interface User {
  id: number;
  name: string;
}

interface Store {
  users: Array<User>;
}

interface Methods {
  users: {
    add: (user: User) => void;
  };
}

const storeWithStorage = useZustand<Store, Methods>({
  keys: ['users'],
  default: {
    users: [],
  },
  methods: {
    users: (use) => ({
      add: (user) => {
        const { state, updater } = use();
        updater({ users: [user, ...state.users.value] });
      },
    }),
  },
  forStorage: {
    storageName: 'users',
  },
});

export type StoreWithStorageTypes = StoreTypes<typeof storeWithStorage.use>;
export default storeWithStorage;

//component
import storeWithStorage from './stores/withStorage';

function App() {
  const users = storeWithStorage.use.users();

  const random = Math.floor(Math.random() * 10000);

  return (
    <>
      <button onClick={() => users.add({ id: random, name: `user-${random}` })}>add</button>
      <button onClick={users.clear}>clear</button>
      <ul>
        {users.value.map((user) => (
          <li key={user.id}>
            <span>{user.name}</span>
          </li>
        ))}
      </ul>
    </>
  );
}

with async

Inside the store you can do any asynchronous action.

// store
import useZustand, { StoreTypes } from 'react-use-zustand';

interface User {
  id: number;
  name: string;
}

interface Store {
  status: 'pending' | 'success' | 'error' | '';
  users: Array<User>;
}

interface Methods {
  users: {
    getAsyncUsers: () => void;
  };
}

const asyncStore = useZustand<Store, Methods>({
  keys: ['users', 'status'],
  default: {
    status: '',
    users: [],
  },
  methods: {
    users: (getState) => ({
      getAsyncUsers: async () => {
        const state = getState();
        if (state.status.value !== 'pending') {
          state.status.set('pending');
          try {
            const response = await fetch('https://jsonplaceholder.typicode.com/users');
            const users: Array<User> = await response.json();
            state.users.set(users);
            state.status.set('success');
          } catch (e) {
            state.status.set('error');
          }
        }
      },
    }),
  },
  forStorage: {
    keys: ['users'],
    storageName: 'users',
  },
});

export type AsyncStoreTypes = StoreTypes<typeof asyncStore.use>;
export default asyncStore;

//component
import asyncStore from './stores/async';

function App() {
  const users = asyncStore.use.users();
  const status = asyncStore.use.status();

  useEffect(() => {
    users.getAsyncUsers();
  }, []);

  return (
    <div>
      <div>{status.value}</div>

      <ul>
        {users.value.map((user) => (
          <li key={user.id}>
            <span>{user.name}</span>
          </li>
        ))}
      </ul>
    </div>
  );
}

outside the component

You can also change state outside of jsx/tsx component.

// store
import useZustand, { StoreTypes } from 'react-use-zustand';

interface User {
  id: number;
  name: string;
}

interface Store {
  users: Array<User>;
}

interface Methods {
  users: {
    getUsers: (users: Array<User>) => void;
  };
}

const asyncStore = useZustand<Store, Methods>({
  keys: ['users'],
  default: {
    users: [],
  },
  methods: {
    users: (getState) => ({
      getUsers: async (users) => {
        const state = getState();
        state.users.set(users);
      },
    }),
  },
});

export type AsyncStoreTypes = StoreTypes<typeof asyncStore.use>;
export default asyncStore;

//component
import asyncStore from './stores/async';

(async function getUsers() {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users');
    const users: Array<User> = await response.json();
    asyncStore.setStateOutsideComponent({
      users,
    });
  } catch (e) {}
})();

function App() {
  const users = asyncStore.use.users();

  return (
    <div>
      <ul>
        {users.value.map((user) => (
          <li key={user.id}>
            <span>{user.name}</span>
          </li>
        ))}
      </ul>
    </div>
  );
}
1.0.19

1 year ago

1.0.18

1 year ago

1.0.17

1 year ago

1.0.16

1 year ago

1.0.22

1 year ago

1.0.21

1 year ago

1.0.20

1 year ago

1.0.25

11 months ago

1.0.24

11 months ago

1.0.15

1 year ago

1.0.14

1 year ago

1.0.13

1 year ago

1.0.12

1 year ago

1.0.11

1 year ago

1.0.10

1 year ago

1.0.9

1 year ago

1.0.8

1 year ago

1.0.7

1 year ago

1.0.6

1 year ago

1.0.5

1 year ago

1.0.4

1 year ago

1.0.3

1 year ago

1.0.2

1 year ago

1.0.1

1 year ago