Skip to content

Commit

Permalink
Merge pull request #6 from react18-tools/standardise
Browse files Browse the repository at this point in the history
Standardise
  • Loading branch information
mayank1513 authored Dec 29, 2023
2 parents dfadffc + 57eefca commit b3a2338
Show file tree
Hide file tree
Showing 24 changed files with 361 additions and 113 deletions.
24 changes: 13 additions & 11 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

title: ""
labels: ""
assignees: ""
---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:

1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
Expand All @@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen.
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]

- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]

**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]

- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]

**Additional context**
Add any other context about the problem here.
7 changes: 3 additions & 4 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

title: ""
labels: ""
assignees: ""
---

**Is your feature request related to a problem? Please describe.**
Expand Down
8 changes: 5 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ jobs:
- run: npm i -g pnpm && pnpm i
name: Install dependencies
- name: Publish to NPM
run: pnpm build && pnpm publish-package
# continue on error to publish scoped package name <- by default repo is setup for a non-scoped + scoped package name
continue-on-error: true
run: |
pnpm build && cd dist && npm publish
sed -i -e "s/.*name.*/\t\"name\": \"zustand-sync\",/" package.json && npm publish
sed -i -e "s/.*name.*/\t\"name\": \"sync-zustand\",/" package.json && npm publish
sed -i -e "s/.*name.*/\t\"name\": \"sync-tabs-zustand\",/" package.json && npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
3 changes: 2 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*lock.*
*lock.*
*.md
2 changes: 1 addition & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"recommendations": ["esbenp.prettier-vscode"]
"recommendations": ["esbenp.prettier-vscode", "mayank1513.trello-kanban-task-board"]
}
79 changes: 44 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
# Zustand Sync Tabs [![Version](https://img.shields.io/npm/v/zustand-sync-tabs.svg?colorB=green)](https://www.npmjs.com/package/zustand-sync-tabs) [![Downloads](https://img.jsdelivr.com/img.shields.io/npm/dt/zustand-sync-tabs.svg)](https://www.npmjs.com/package/zustand-sync-tabs) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/zustand-sync-tabs)

> Zustand middleware to easily sync Zustand state between tabs / windows / iframes (Same Origin)
> Zustand middleware to easily sync Zustand state between tabs/windows / iframes (Same Origin)
- ✅ 🐙 (465 Bytes gZiped) < 0.5 kB size cross-tab state sharing middleware for zustand
- ✅ 🐙 ~ 1 kB size cross-tab state sharing middleware for Zustand
- ✅ Full TypeScript Support
- ✅ solid reliability in 1 writing and n reading tab-scenarios (with changing writing tab)
- ✅ Fire and forget approach of always using the latest state. Perfect for single user systems
- ✅ solid reliability in 1 writing and n reading tab scenarios (with changing writing tab)
- ✅ Fire and forget approach of always using the latest state. Perfect for single-user systems
- ✅ Sync Zustand state between multiple browsing contexts
- ✅ Partial state sharing also supported
- ✅ Partial state sharing is also supported

> Checkout `[persistnsync](https://github.com/mayank1513/nextjs-themes/tree/main/packages/persistnsync#readme)` if you are looking for persisting state locally over reload/refresh or after closing site
> Check out `[persist-and-sync](https://github.com/react18-tools/persist-and-sync)` if you are looking for persisting state locally over reload/refresh or after closing the site.
## Install

```bash
$ pnpm add zustand-sync-tabs
# or
```
**or**

```bash
$ npm install zustand-sync-tabs
# or
```
**or**

```bash
$ yarn add zustand-sync-tabs
```

## Usage

Simply add the middleware while creating the store and the rest will be taken care.
Add the middleware while creating the store and the rest will be taken care.

```ts
import { create } from "zustand";
Expand All @@ -49,40 +55,43 @@ const useStore = create<MyStore>(

## Advanced - ignore / filter out fields based on regExp

In several cases you might want to exclude several fields from syncing. To support this scenario, we provide a mechanism to exclude fields based on regExp. Just pass `regExpToIgnore` (optional - default -> undefined) in options object.
In several cases you might want to exclude several fields from syncing. To support this scenario, we provide a mechanism to exclude fields based on list of fields or regular expression.

```ts
// to ignore fields containing a slug
persistNSync(
set => ({
count: 0,
slugSomeState: 1,
slugSomeState2: 1,
set: n => set({ count: n }),
}),
{ name: "my-channel", regExpToIgnore: /slug/ },
// or regExpToIgnore: new RegExp('slug')
// Use full power of regExp by adding `i` and `g` flags
),
type SyncTabsOptionsType = {
name: string;
/** @deprecated */
regExpToIgnore?: RegExp;
include?: (string | RegExp)[];
exclude?: (string | RegExp)[];
};
```

For more details about regExp check out - [JS RegExp](https://www.w3schools.com/jsref/jsref_obj_regexp.asp)

### Exact match

For exactly matching a parameter/field use `/^your-field-name$/`. `^` forces match from the first caracter and similarly, `$` forces match until last character.
**Example**

### Ignore multiple fields with exact match

use `regExpToIgnore: /^(field1|field2|field3)$/`

## Roadmap
```typescript
export const useMyStore = create<MyStoreType>()(
syncTabs(
set => ({
count: 0,
_count: 0 /** skipped as it is included in exclude array */,
setCount: count => {
set(state => ({ ...state, count }));
},
set_Count: _count => {
set(state => ({ ...state, _count }));
},
}),
{ name: "example", exclude: ["_count"] },
),
);
```

- [ ] `regExpToInclude` -> once implemented, passing this parameter will sync only matching fields
For more details about regExp check out - [JS RegExp](https://www.w3schools.com/jsref/jsref_obj_regexp.asp)

### 🤩 Don't forger to start [this repo](https://github.com/mayank1513/turborepo-template)!
### 🤩 Don't forget to star [this repo](https://github.com/mayank1513/turborepo-template)!

Want handson course for getting started with Turborepo? Check out [React and Next.js with TypeScript](https://mayank-chaudhari.vercel.app/courses/react-and-next-js-with-typescript) and [The Game of Chess with Next.js, React and TypeScrypt](https://www.udemy.com/course/game-of-chess-with-nextjs-react-and-typescrypt/?referralCode=851A28F10B254A8523FE)
Want hands-on course for getting started with Turborepo? Check out [React and Next.js with TypeScript](https://mayank-chaudhari.vercel.app/courses/react-and-next-js-with-typescript) and [The Game of Chess with Next.js, React and TypeScrypt](https://www.udemy.com/course/game-of-chess-with-nextjs-react-and-typescrypt/?referralCode=851A28F10B254A8523FE)

![Alt](https://repobeats.axiom.co/api/embed/6b5fa6a5fbb6affafea042ba0f292ecf9388ef3c.svg "Repobeats analytics image")

Expand Down
3 changes: 3 additions & 0 deletions examples/nextjs/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
module.exports = {
extends: ["custom/next"],
rules: {
"@typescript-eslint/explicit-function-return-type": "off",
},
};
7 changes: 7 additions & 0 deletions examples/nextjs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# nextjs

## 1.0.2

### Patch Changes

- Updated dependencies [2894d47]
- [email protected]

## 1.0.1

### Patch Changes
Expand Down
8 changes: 7 additions & 1 deletion examples/nextjs/app/NotSyncedCounter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ export default function NotSyncedCounter() {
return (
<div className={styles.card}>
<h2>Not Synced Counter:</h2>
<button onClick={() => setCount2(_count + 1)}>🖤 {_count}</button>
<button
onClick={() => {
setCount2(_count + 1);
}}
type="button">
🖤 {_count}
</button>
</div>
);
}
5 changes: 4 additions & 1 deletion examples/nextjs/app/OpenNewTab.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"use client";
export default function OpenNewTab() {
return (
<button className="btn btn-outline mt-4" onClick={() => window.open(".", "_blank")}>
<button
className="btn btn-outline mt-4"
onClick={() => window.open(".", "_blank")}
type="button">
Open in new tab/window
</button>
);
Expand Down
8 changes: 7 additions & 1 deletion examples/nextjs/app/SyncedCounter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ export default function SyncedCounter() {
return (
<div className={styles.card}>
<h2>Synced Counter:</h2>
<button onClick={() => setCount(count + 1)}>💖 {count}</button>
<button
onClick={() => {
setCount(count + 1);
}}
type="button">
💖 {count}
</button>
</div>
);
}
18 changes: 9 additions & 9 deletions examples/nextjs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nextjs",
"version": "1.0.1",
"version": "1.0.2",
"private": true,
"scripts": {
"dev": "next dev --port 3001",
Expand All @@ -9,19 +9,19 @@
"lint": "next lint"
},
"dependencies": {
"next": "^13.4.19",
"next": "^14.0.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"zustand-sync-tabs": "workspace:*",
"zustand": "^4.4.1"
"zustand": "^4.4.7",
"zustand-sync-tabs": "workspace:*"
},
"devDependencies": {
"@next/eslint-plugin-next": "^13.4.19",
"@types/node": "^17.0.12",
"@types/react": "^18.0.22",
"@types/react-dom": "^18.0.7",
"@next/eslint-plugin-next": "^14.0.4",
"@types/node": "^20.10.5",
"@types/react": "^18.2.46",
"@types/react-dom": "^18.2.18",
"eslint-config-custom": "workspace:*",
"tsconfig": "workspace:*",
"typescript": "^4.5.3"
"typescript": "^5.3.3"
}
}
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
"build": "turbo run build",
"dev": "turbo run dev",
"lint": "turbo run lint",
"test": "turbo run test",
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,md,css,scss}\""
},
"devDependencies": {
"@changesets/cli": "^2.26.2",
"eslint": "^8.47.0",
"prettier": "^3.0.2",
"@changesets/cli": "^2.27.1",
"eslint": "^8.56.0",
"prettier": "^3.1.1",
"tsconfig": "workspace:*",
"turbo": "latest"
"turbo": "^1.11.2"
},
"packageManager": "[email protected]",
"name": "turbo-template"
Expand Down
4 changes: 2 additions & 2 deletions packages/eslint-config-custom/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "0.0.0",
"private": true,
"devDependencies": {
"@vercel/style-guide": "^4.0.2",
"eslint-config-turbo": "^1.10.12"
"@vercel/style-guide": "^5.1.0",
"eslint-config-turbo": "^1.11.2"
}
}
6 changes: 6 additions & 0 deletions packages/zustand-sync-tabs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# zustand-sync-tabs

## 0.2.0

### Minor Changes

- 2894d47: Add include and exclude options

## 0.1.0

### Minor Changes
Expand Down
24 changes: 24 additions & 0 deletions packages/zustand-sync-tabs/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { act, cleanup, renderHook } from "@testing-library/react";

import { afterEach, describe, test } from "vitest";
import { useCookieStore, useMyStore } from "./store";

describe.concurrent("Setting state", () => {
afterEach(cleanup);
test("test initial state", async ({ expect }) => {
const { result } = renderHook(() => useMyStore());
expect(result.current.count).toBe(0);
});

test("test setting state", async ({ expect }) => {
const { result } = renderHook(() => useCookieStore());
act(() => result.current.setCount(5));
expect(result.current.count).toBe(5);
});

test("test exclude key", async ({ expect }) => {
const { result } = renderHook(() => useCookieStore());
act(() => result.current.set_Count(6));
expect(result.current._count).toBe(6);
});
});
33 changes: 33 additions & 0 deletions packages/zustand-sync-tabs/__tests__/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { create } from "../vitest.setup";
import { syncTabs } from "../src";

type MyStoreType = {
count: number;
_count: number;
setCount: (c: number) => void;
set_Count: (c: number) => void;
};

export const useMyStore = create<MyStoreType>(
syncTabs(
set => ({
count: 0,
_count: 0 /** skipped as it matches the regexp provided */,
setCount: count => set(state => ({ ...state, count })),
set_Count: _count => set(state => ({ ...state, _count })),
}),
{ name: "example", exclude: [/^_/] },
),
);

export const useCookieStore = create<MyStoreType>(
syncTabs(
set => ({
count: 0,
_count: 0 /** skipped as it matches the regexp provided */,
setCount: count => set(state => ({ ...state, count })),
set_Count: _count => set(state => ({ ...state, _count })),
}),
{ name: "example", include: [/count/], exclude: [/^_/] },
),
);
Loading

0 comments on commit b3a2338

Please sign in to comment.