├── .gitignore
├── README.md
├── assets
└── devtools.png
├── example
└── index.ts
├── package-lock.json
├── package.json
├── src
└── index.tsx
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .DS_Store
3 | node_modules
4 | .rts2_cache_cjs
5 | .rts2_cache_es
6 | .rts2_cache_umd
7 | dist
8 | .size-snapshot.json
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # simple-zustand-devtools
2 |
3 | Inspect your [zustand](https://github.com/react-spring/zustand) store in React DevTools 🐻⚛️
4 |
5 |
6 |
7 | ## Usage
8 |
9 | ```ts
10 | import create from 'zustand';
11 | import { mountStoreDevtool } from 'simple-zustand-devtools';
12 |
13 | export const useStore = create(set => {
14 | // create your zustand store here
15 | });
16 |
17 | if (process.env.NODE_ENV === 'development') {
18 | mountStoreDevtool('Store', useStore);
19 | }
20 | ```
21 |
22 | ### Mount more than one store
23 |
24 | `mountStoreDevtool` creates a new HTML element with `id`: `simple-zustand-devtools-${storeName}`, where `storeName` is a name provided as the first argument. You can mount more than one store, as long as store names remain unique. For example:
25 |
26 | ```ts
27 | import create from 'zustand';
28 | import { mountStoreDevtool } from 'simple-zustand-devtools';
29 |
30 | export const useStore1 = create((set, get) => {
31 | // create your zustand store here
32 | });
33 |
34 | export const useStore2 = create((set, get) => {
35 | // create your zustand store here
36 | });
37 |
38 | if (process.env.NODE_ENV === 'development') {
39 | mountStoreDevtool('Store1', useStore1);
40 |
41 | mountStoreDevtool('Store2', useStore2);
42 | }
43 | ```
44 |
45 | ## Installation
46 |
47 | ### For React 18+
48 |
49 | ```sh
50 | yarn add simple-zustand-devtools --dev
51 | ```
52 |
53 | ### For React 17
54 |
55 | Use [a 1.0.1 release (or lower)](https://www.npmjs.com/package/simple-zustand-devtools?activeTab=versions) for React 17.
56 |
57 | ```sh
58 | npm install simple-zustand-devtools@1.0.1 --save-dev --legacy-peer-deps
59 | ```
60 |
61 | ## Docs
62 |
63 | ### mountStoreDevtool
64 |
65 | ```ts
66 | import { StoreApi } from 'zustand';
67 |
68 | type ZustandStore = StoreApi>;
69 |
70 | export function mountStoreDevtool(
71 | storeName: string,
72 | store: ZustandStore,
73 | rootElement?: HTMLElement
74 | ): void;
75 | ```
76 |
77 | ## Local Development
78 |
79 | Below is a list of commands you will probably find useful.
80 |
81 | ### `npm start` or `yarn start`
82 |
83 | Runs the project in development/watch mode. Project will be rebuilt upon changes.
84 |
85 | ### `npm run build` or `yarn build`
86 |
87 | Bundles the package to the `dist` folder.
88 | The package is optimized and bundled with Rollup into multiple formats (CommonJS, UMD, and ES Module).
89 |
90 | ### `npm test` or `yarn test`
91 |
92 | Runs the test watcher (Jest) in an interactive mode.
93 | By default, runs tests related to files changed since the last commit.
94 |
--------------------------------------------------------------------------------
/assets/devtools.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beerose/simple-zustand-devtools/46fbf79f675c6e05eb53ff32f30e22dc7be5b91a/assets/devtools.png
--------------------------------------------------------------------------------
/example/index.ts:
--------------------------------------------------------------------------------
1 | import create from 'zustand';
2 | import { mountStoreDevtool } from '../src';
3 |
4 | export const useStore1 = create((_set, _get) => {
5 | return {};
6 | });
7 |
8 | export const useStore2 = create((_set, _get) => {
9 | return {};
10 | });
11 |
12 | if (process.env.NODE_ENV === 'development') {
13 | mountStoreDevtool('Store1', useStore1);
14 |
15 | mountStoreDevtool('Store2', useStore2);
16 | }
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simple-zustand-devtools",
3 | "author": "Aleksandra Sikora",
4 | "version": "1.1.0",
5 | "license": "MIT",
6 | "main": "dist/index.js",
7 | "types": "dist/index.d.ts",
8 | "umd:main": "dist/simple-zustand-devtools.umd.production.js",
9 | "module": "dist/simple-zustand-devtools.es.production.js",
10 | "typings": "dist/index.d.ts",
11 | "files": [
12 | "dist"
13 | ],
14 | "description": "Devtools for Zustand",
15 | "repository": "beerose/simple-zustand-devtools",
16 | "keywords": [
17 | "react",
18 | "zustand",
19 | "devtools"
20 | ],
21 | "scripts": {
22 | "start": "tsdx watch",
23 | "build": "tsdx build",
24 | "test": "tsdx test"
25 | },
26 | "peerDependencies": {
27 | "@types/react": ">=18.0.0",
28 | "@types/react-dom": ">=18.0.0",
29 | "react": ">=18.0.0",
30 | "react-dom": ">=18.0.0",
31 | "zustand": ">=1.0.2"
32 | },
33 | "prettier": {
34 | "printWidth": 80,
35 | "semi": true,
36 | "singleQuote": true,
37 | "trailingComma": "es5"
38 | },
39 | "devDependencies": {
40 | "@types/react": "^18.0.9",
41 | "@types/react-dom": "^18.0.4",
42 | "prettier": "^1.17.0",
43 | "pretty-quick": "^1.10.0",
44 | "tsdx": "^0.5.6",
45 | "typescript": "^3.9.10",
46 | "zustand": "^3.5.10"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from 'react';
2 | import { createRoot } from 'react-dom/client';
3 | import { UseStore } from 'zustand';
4 |
5 | export function mountStoreDevtool>(
6 | storeName: string,
7 | store: UseStore,
8 | rootElement?: HTMLElement
9 | ) {
10 | type StoreState = ReturnType['getState']>;
11 |
12 | const externalUpdates = {
13 | count: 0,
14 | };
15 |
16 | const ZustandDevtool: React.FC = props => {
17 | const allUpdatesCount = useRef(externalUpdates.count);
18 |
19 | useEffect(() => {
20 | allUpdatesCount.current += 1;
21 | if (allUpdatesCount.current === externalUpdates.count + 1) {
22 | allUpdatesCount.current -= 1;
23 |
24 | // DevTools update
25 | store.setState(props);
26 | }
27 | });
28 |
29 | return null;
30 | };
31 |
32 | (ZustandDevtool as any).displayName = `((${storeName})) devtool`;
33 |
34 | if (typeof document === 'undefined') {
35 | return;
36 | }
37 |
38 | if (!rootElement) {
39 | let root = document.getElementById(`simple-zustand-devtools-${storeName}`);
40 | if (!root) {
41 | root = document.createElement('div');
42 | root.id = `simple-zustand-devtools-${storeName}`;
43 | }
44 |
45 | document.body.appendChild(root);
46 | rootElement = root;
47 | }
48 | const newRoot = createRoot(rootElement)
49 | const renderDevtool = (state: StoreState | void) => {
50 | if (!state) {
51 | return;
52 | }
53 | newRoot.render();
54 | externalUpdates.count += 1;
55 | };
56 |
57 | renderDevtool(store.getState());
58 | store.subscribe(renderDevtool);
59 | }
60 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["src", "types"],
3 | "compilerOptions": {
4 | "target": "es5",
5 | "module": "esnext",
6 | "lib": ["dom", "esnext"],
7 | "importHelpers": true,
8 | "declaration": true,
9 | "sourceMap": true,
10 | "rootDir": "./",
11 | "strict": true,
12 | "noImplicitAny": true,
13 | "strictNullChecks": true,
14 | "strictFunctionTypes": true,
15 | "strictPropertyInitialization": true,
16 | "noImplicitThis": true,
17 | "alwaysStrict": true,
18 | "noUnusedLocals": true,
19 | "noUnusedParameters": true,
20 | "noImplicitReturns": true,
21 | "noFallthroughCasesInSwitch": true,
22 | "moduleResolution": "node",
23 | "baseUrl": "./",
24 | "paths": {
25 | "*": ["src/*", "node_modules/*"]
26 | },
27 | "jsx": "react",
28 | "esModuleInterop": true
29 | }
30 | }
31 |
--------------------------------------------------------------------------------