Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
attitude committed Mar 13, 2023
0 parents commit 65d358b
Show file tree
Hide file tree
Showing 43 changed files with 3,351 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = tab
insert_final_newline = true
trim_trailing_whitespace = true

[*.{yml,yaml}]
indent_style = space
4 changes: 4 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/build
/dist
/node_modules
/temp
37 changes: 37 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module.exports = {
extends: [
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/jsx-runtime',
'plugin:typescript-sort-keys/recommended',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: { jsx: true },
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: [
'react',
'@typescript-eslint',
'react-hooks',
'sort-keys-fix',
],
rules: {
'@typescript-eslint/no-unused-vars': 'warn',
'comma-dangle': ['error', 'always-multiline'],
'no-unused-vars': 'off',
quotes: ['error', 'single', { 'avoidEscape': true }],
'react/jsx-sort-props': ['error', { shorthandFirst: true }],
'react/prop-types': 'off',
'react-hooks/exhaustive-deps': 'warn',
'react-hooks/rules-of-hooks': 'error',
semi: ['error', 'never'],
'sort-keys-fix/sort-keys-fix': ['error', 'asc', { natural: true }],
},
settings: {
react: {
version: 'detect',
},
},
}
77 changes: 77 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: CI

# Controls when the workflow will run
on:
push:
branches: [main]
tags:
- "*"
pull_request:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: "publish"
cancel-in-progress: true

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- run: npm install -g pnpm
- run: pnpm install
- run: pnpm test && pnpm build:packages

publish-web:
if: github.ref_type != 'tag'
needs: [test]
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}

runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v3
- run: npm install -g pnpm
- run: pnpm install
- run: pnpm run build
- name: Setup Pages
uses: actions/configure-pages@v1
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
path: apps/web/dist
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1

publish-npm:
if: github.event_name == 'push' && github.ref_type == 'tag'
needs: [test]
environment:
name: npm

runs-on: ubuntu-latest

env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

steps:
- name: Checkout
uses: actions/checkout@v3
- run: npm install -g pnpm
- run: pnpm install
- run: pnpm run build:packages
- run: pnpm config set "//registry.npmjs.org/:_authToken" "$NPM_TOKEN"
- run: pnpm config set "git-tag-version" "false"
- run: pnpm recursive exec npm version "${GITHUB_REF:11}"
- run: pnpm recursive publish --access public --no-git-checks
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
build
dist
node_modules
temp
3 changes: 3 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
strict-peer-dependencies=false
auto-install-peers=true
git-tag-version=false
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
build
dist
node_modules
temp
18 changes: 18 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"arrowParens": "avoid",
"bracketSpacing": true,
"printWidth": 120,
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all",
"useTabs": true,
"overrides": [
{
"files": "tsconfig.json",
"options": {
"printWidth": 80
}
}
]
}
6 changes: 6 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"recommendations": [
"arcanis.vscode-zipfs",
"dbaeumer.vscode-eslint"
]
}
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2023 Martin Adamko

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
84 changes: 84 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# react-selector-context

Tiny selector library for React Context to fix unnecessary re-renders

## Usage

1. Wrap existing React context with `createSelectorContext(yourContext)`
2. Replace original `useContext()` hook with a new one returned with a selector
3. Replace original `Context.Provider` with a new one returned
4. Profit!

```tsx
import { memo, createContext, useMemo, useState } from 'react'
import { createSelectorContext } from 'react-selector-context'

type CountersStore = {
counter: number;
otherCounter: number;
}

const Counters = createContext<CountersStore>({
counter: 0,
otherCounter: 0,
})

// Step 1:
export const [Provider, useSelectorContext] = createSelectorContext(Context)

const selectCounter = (context: CountersStore) => context.counter
const selectOtherCounter = (context: CountersStore) => context.otherCounter

const Counter = memo(() => {
// Step 2:
const value = useSelectorContext(selectCounter)

const renders = useRef(0)
renders.current = renders.current + 1

return <span>Count: {value}, renders: {renders.current}</span>
})

const OtherCounter = memo(() => {
// Step 2:
const value = useSelectorContext(selectOtherCounter)

const renders = useRef(0)
renders.current = renders.current + 1

return <span>Other Count: {value}, renders: {renders.current}</span>
})

export default App() {
const [counter, setCounter] = useState(0)
const [otherCounter, setOtherCounter] = useState(0)

const value = useMemo(() => ({
counter,
otherCounter,
setCounter,
setOtherCounter,
}), [counter, otherCounter ])

// Step 3:
return (
<div>
<Provider value={value}>
<SelectorCounter />
<button onClick={() => {
setCounter(value => value + 1)
}}>
Increase counter
</button>

<OtherSelectorCounter />
<button onClick={() => { setOtherCounter(value => value + 1) }}>
Increase other counter
</button>
</Provider>
</div>
)
}
```

[See the full example app &rarr;](apps/web/src/Components/SelectorCounters.tsx)
16 changes: 16 additions & 0 deletions apps/web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>react-selector-context</title>
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>

</html>
20 changes: 20 additions & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "web",
"version": "0.0.0",
"type": "module",
"private": true,
"scripts": {
"start": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"test": "echo \"TODO\""
},
"repository": {
"type": "git",
"url": "git+https://github.com/attitude/react-selector-context.git",
"directory": "apps/web"
},
"dependencies": {
"react-selector-context": "workspace:*"
}
}
1 change: 1 addition & 0 deletions apps/web/public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
57 changes: 57 additions & 0 deletions apps/web/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
html, body {
margin: 0;
padding: 0;
font-family: system-ui;
}

body {
display: flex;
flex-direction: row;
justify-content: center;
}

main {
max-width: 30em;
}

*, *::after, *::before {
box-sizing: border-box;
}

:where(h1, h2, h3, h4, h5, h6, p, pre):first-child { margin-top: 0 }
:where(h1, h2, h3, h4, h5, h6, p, pre):last-child { margin-bottom: 0 }

@media (prefers-color-scheme: dark) { * { color-scheme: dark; } }
@media (prefers-color-scheme: light) { * { color-scheme: light; } }

.border { border: 1px solid }
.no-border { border: none }
.border-radius { border-radius: 0.5em }
.box-radius { border-radius: 1em }

.vertical-stack {
display: flex;
flex-direction: column;
}

.horizontal-stack {
display: flex;
flex-direction: row;
}

.gap { gap: 0.5em }
.gutter { gap: 2em }

.align-center { align-items: center; text-align: center }

.justify-center { justify-content: center }
.justify-space-between { justify-content: space-between }
.justify-space-around { justify-content: space-around }

.padding { padding: 0.5em }
.box-padding { padding: 1em }

.has-rendered {
background-color: rgba(255, 0, 0, 0.25);
outline: 2px solid red;
}
Loading

0 comments on commit 65d358b

Please sign in to comment.