├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .vscode └── tasks.json ├── README.md ├── package-lock.json ├── package.json ├── tsconfig.json └── useLiveState.ts /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: push 4 | 5 | jobs: 6 | test: 7 | name: Build and run tests 8 | runs-on: ubuntu-20.04 9 | 10 | steps: 11 | - name: Checkout project 12 | uses: actions/checkout@v3 13 | - name: Install dependencies 14 | run: npm install 15 | - name: Build 16 | run: npm run build -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | build 3 | node_modules -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "mix test", 6 | "type": "shell", 7 | "command": "mix", 8 | "args": [ 9 | "test", 10 | "--color" 11 | ], 12 | "options": { 13 | "cwd": "${workspaceRoot}", 14 | "requireFiles": [ 15 | "test/**/test_helper.exs", 16 | "test/**/*_test.exs" 17 | ] 18 | }, 19 | "problemMatcher": "$mixTestFailure" 20 | }, 21 | { 22 | "label": "mix test failed", 23 | "type": "shell", 24 | "command": "mix", 25 | "args": [ 26 | "test", 27 | "--color", 28 | "--failed", 29 | "--trace" 30 | ], 31 | "options": { 32 | "cwd": "${workspaceRoot}", 33 | "requireFiles": [ 34 | "test/**/test_helper.exs", 35 | "test/**/*_test.exs" 36 | ] 37 | }, 38 | "problemMatcher": "$mixTestFailure" 39 | }, 40 | { 41 | "label": "mix test file", 42 | "type": "shell", 43 | "command": "mix", 44 | "args": [ 45 | "test", 46 | "${relativeFile}", 47 | "--color", 48 | "--trace" 49 | ], 50 | "options": { 51 | "cwd": "${workspaceRoot}", 52 | "requireFiles": [ 53 | "test/**/test_helper.exs", 54 | "test/**/*_test.exs" 55 | ] 56 | }, 57 | "problemMatcher": "$mixTestFailure" 58 | }, 59 | { 60 | "label": "run testbed test at cursor", 61 | "type": "shell", 62 | "command": "mix", 63 | "args": [ 64 | "test", 65 | "../${relativeFile}:${lineNumber}", 66 | "--color", 67 | "--trace" 68 | ], 69 | "options": { 70 | "cwd": "${workspaceRoot}/testbed", 71 | "requireFiles": [ 72 | "test/**/test_helper.exs", 73 | "test/**/*_test.exs" 74 | ], 75 | "env": { 76 | "GCP_CREDENTIALS": "{\"stuff\": \"foo\"}" 77 | } 78 | }, 79 | "problemMatcher": "$mixTestFailure", 80 | "group": { 81 | "kind": "test", 82 | "isDefault": true 83 | } 84 | }, 85 | { 86 | "label": "Initial Setup", 87 | "type": "process", 88 | "command": "mix", 89 | "args": [ 90 | "setup" 91 | ], 92 | "options": { 93 | "cwd": "${workspaceRoot}" 94 | }, 95 | "problemMatcher": [ 96 | "$mixCompileError" 97 | ] 98 | }, 99 | { 100 | "label": "mix compile", 101 | "type": "process", 102 | "command": "mix", 103 | "args": [ 104 | "test", 105 | "--exclude", 106 | "test", 107 | "--warnings-as-errors" 108 | ], 109 | "options": { 110 | "cwd": "${workspaceRoot}" 111 | }, 112 | "problemMatcher": [ 113 | "$mixCompileWarning", 114 | "$mixCompileError" 115 | ], 116 | "group": { 117 | "kind": "build", 118 | "isDefault": true 119 | } 120 | }, 121 | { 122 | "label": "mix format", 123 | "type": "process", 124 | "command": "mix", 125 | "args": [ 126 | "format" 127 | ], 128 | "options": { 129 | "cwd": "${workspaceRoot}" 130 | }, 131 | "problemMatcher": [ 132 | "$mixCompileWarning", 133 | "$mixCompileError" 134 | ], 135 | "group": { 136 | "kind": "build", 137 | "isDefault": true 138 | } 139 | }, 140 | { 141 | "label": "mix check", 142 | "type": "process", 143 | "command": "mix", 144 | "args": [ 145 | "check" 146 | ], 147 | "options": { 148 | "cwd": "${workspaceRoot}" 149 | }, 150 | "problemMatcher": [ 151 | "$mixCompileWarning", 152 | "$mixCompileError" 153 | ], 154 | "group": { 155 | "kind": "build", 156 | "isDefault": true 157 | } 158 | } 159 | ] 160 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # use-lives-state 2 | 3 | This package provides a lil tiny react hook to facilitate React components that have their state managed by LiveState. 4 | 5 | ## Usage 6 | 7 | Add it just like any npm: 8 | 9 | ``` 10 | npm install use-live-state 11 | ``` 12 | 13 | Make a LiveState instance: 14 | 15 | ```typescript 16 | import LiveState from "phx-live-state"; 17 | 18 | export const liveState = new LiveState('ws://localhost:4002/socket', 'todo:all'); 19 | ``` 20 | 21 | Then, in your components: 22 | 23 | ```typescript 24 | import React, { Component } from 'react'; 25 | import { liveState } from './live_state'; 26 | import useLiveState from 'use-live-state'; 27 | 28 | 29 | export const TodoList = () => { 30 | 31 | const [state, _pushEvent] = useLiveState(liveState, {}); 32 | 33 | return ( 34 | 37 | ); 38 | } 39 | ``` 40 | 41 | ```typescript 42 | import React, { Component, useRef } from 'react'; 43 | import { liveState } from './live_state_react'; 44 | import useLiveState from 'use-live-state'; 45 | 46 | export const TodoForm = () => { 47 | 48 | const input = useRef(null); 49 | 50 | const [_state, pushEvent] = useLiveState(liveState, {}); 51 | 52 | const onButtonClick = () => { 53 | pushEvent('add_todo', {todo: input.current.value}); 54 | input.current.value = ''; 55 | }; 56 | 57 | return ( 58 |
59 | 60 | 61 |
62 | ); 63 | } 64 | ``` 65 | 66 | To see an example project of how all this fits together, check out https://github.com/launchscout/live_state/tree/main/testbed 67 | 68 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "use-live-state", 3 | "version": "0.0.2", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "use-live-state", 9 | "version": "0.0.2", 10 | "license": "ISC", 11 | "dependencies": { 12 | "phx-live-state": ">= 0.9.0" 13 | }, 14 | "devDependencies": { 15 | "react": ">= 16.8.0", 16 | "typescript": "^4.8.4" 17 | }, 18 | "peerDependencies": { 19 | "react": ">= 16.8.0" 20 | } 21 | }, 22 | "../phx-live-state": { 23 | "version": "0.8.0", 24 | "extraneous": true, 25 | "license": "MIT", 26 | "dependencies": { 27 | "json-joy": "^1.18.1", 28 | "lit": "^2.2.6", 29 | "phoenix": "1.6.15", 30 | "reflect-metadata": "^0.1.13", 31 | "wc-context": "launchscout/wc-context" 32 | }, 33 | "devDependencies": { 34 | "@esm-bundle/chai": "^4.3.4-fix.0", 35 | "@open-wc/testing": "^3.1.6", 36 | "@types/sinon-chai": "^3.2.8", 37 | "@web/test-runner": "^0.13.31", 38 | "@web/test-runner-puppeteer": "^0.10.5", 39 | "fast-json-patch": "^3.1.1", 40 | "quibble": "^0.6.12", 41 | "sinon": "^14.0.0", 42 | "sinon-chai": "^3.7.0", 43 | "testdouble": "^3.16.6", 44 | "typedoc": "^0.23.24", 45 | "typescript": "^4.2.2" 46 | } 47 | }, 48 | "node_modules/@lit-labs/ssr-dom-shim": { 49 | "version": "1.0.0", 50 | "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.0.0.tgz", 51 | "integrity": "sha512-ic93MBXfApIFTrup4a70M/+ddD8xdt2zxxj9sRwHQzhS9ag/syqkD8JPdTXsc1gUy2K8TTirhlCqyTEM/sifNw==" 52 | }, 53 | "node_modules/@lit/reactive-element": { 54 | "version": "1.6.1", 55 | "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.1.tgz", 56 | "integrity": "sha512-va15kYZr7KZNNPZdxONGQzpUr+4sxVu7V/VG7a8mRfPPXUyhEYj5RzXCQmGrlP3tAh0L3HHm5AjBMFYRqlM9SA==", 57 | "dependencies": { 58 | "@lit-labs/ssr-dom-shim": "^1.0.0" 59 | } 60 | }, 61 | "node_modules/@types/trusted-types": { 62 | "version": "2.0.2", 63 | "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", 64 | "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" 65 | }, 66 | "node_modules/arg": { 67 | "version": "5.0.2", 68 | "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", 69 | "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" 70 | }, 71 | "node_modules/fast-diff": { 72 | "version": "1.2.0", 73 | "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", 74 | "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", 75 | "peer": true 76 | }, 77 | "node_modules/hyperdyperid": { 78 | "version": "1.2.0", 79 | "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", 80 | "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", 81 | "engines": { 82 | "node": ">=10.18" 83 | } 84 | }, 85 | "node_modules/js-tokens": { 86 | "version": "4.0.0", 87 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 88 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 89 | "dev": true 90 | }, 91 | "node_modules/json-joy": { 92 | "version": "1.19.0", 93 | "resolved": "https://registry.npmjs.org/json-joy/-/json-joy-1.19.0.tgz", 94 | "integrity": "sha512-oILIyxHJu52hQ9BFs1yFMXVKrP2cTYOdljRiNRElHzpcSnA03sSf+VKzwqEE2hGKKL1Zvml71b1wPTAQUl8CHQ==", 95 | "dependencies": { 96 | "arg": "^5.0.2", 97 | "hyperdyperid": "^1.2.0" 98 | }, 99 | "bin": { 100 | "json-pack": "bin/json-pack.js", 101 | "json-pack-test": "bin/json-pack-test.js", 102 | "json-patch": "bin/json-patch.js", 103 | "json-patch-test": "bin/json-patch-test.js", 104 | "json-pointer": "bin/json-pointer.js", 105 | "json-pointer-test": "bin/json-pointer-test.js", 106 | "json-unpack": "bin/json-unpack.js" 107 | }, 108 | "engines": { 109 | "node": ">=10.0" 110 | }, 111 | "peerDependencies": { 112 | "quill-delta": "^5", 113 | "rxjs": "7", 114 | "tslib": "2" 115 | } 116 | }, 117 | "node_modules/lit": { 118 | "version": "2.6.1", 119 | "resolved": "https://registry.npmjs.org/lit/-/lit-2.6.1.tgz", 120 | "integrity": "sha512-DT87LD64f8acR7uVp7kZfhLRrHkfC/N4BVzAtnw9Yg8087mbBJ//qedwdwX0kzDbxgPccWRW6mFwGbRQIxy0pw==", 121 | "dependencies": { 122 | "@lit/reactive-element": "^1.6.0", 123 | "lit-element": "^3.2.0", 124 | "lit-html": "^2.6.0" 125 | } 126 | }, 127 | "node_modules/lit-element": { 128 | "version": "3.2.2", 129 | "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.2.2.tgz", 130 | "integrity": "sha512-6ZgxBR9KNroqKb6+htkyBwD90XGRiqKDHVrW/Eh0EZ+l+iC+u+v+w3/BA5NGi4nizAVHGYvQBHUDuSmLjPp7NQ==", 131 | "dependencies": { 132 | "@lit/reactive-element": "^1.3.0", 133 | "lit-html": "^2.2.0" 134 | } 135 | }, 136 | "node_modules/lit-html": { 137 | "version": "2.6.1", 138 | "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.6.1.tgz", 139 | "integrity": "sha512-Z3iw+E+3KKFn9t2YKNjsXNEu/LRLI98mtH/C6lnFg7kvaqPIzPn124Yd4eT/43lyqrejpc5Wb6BHq3fdv4S8Rw==", 140 | "dependencies": { 141 | "@types/trusted-types": "^2.0.2" 142 | } 143 | }, 144 | "node_modules/lodash.clonedeep": { 145 | "version": "4.5.0", 146 | "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", 147 | "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", 148 | "peer": true 149 | }, 150 | "node_modules/lodash.isequal": { 151 | "version": "4.5.0", 152 | "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", 153 | "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", 154 | "peer": true 155 | }, 156 | "node_modules/loose-envify": { 157 | "version": "1.4.0", 158 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 159 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 160 | "dev": true, 161 | "dependencies": { 162 | "js-tokens": "^3.0.0 || ^4.0.0" 163 | }, 164 | "bin": { 165 | "loose-envify": "cli.js" 166 | } 167 | }, 168 | "node_modules/phoenix": { 169 | "version": "1.6.15", 170 | "resolved": "https://registry.npmjs.org/phoenix/-/phoenix-1.6.15.tgz", 171 | "integrity": "sha512-O6AG5jTkZOOkdd/GOSCsM4v3bzBoyRnC5bEi57KhX/Daba6FvnBRzt0nhEeRRiVQGLSxDlyb0dUe9CkYWMZd8g==" 172 | }, 173 | "node_modules/phx-live-state": { 174 | "version": "0.9.0", 175 | "resolved": "https://registry.npmjs.org/phx-live-state/-/phx-live-state-0.9.0.tgz", 176 | "integrity": "sha512-X9VppKb5tpXswdRn8qnEALueRSDYT/fQRIqdYCnLn9Q7C8jkTHs2Q4QRZ9HHS42KX7cQib3CmRDo6uORPzzZJQ==", 177 | "dependencies": { 178 | "json-joy": "^1.18.1", 179 | "lit": "^2.2.6", 180 | "phoenix": "1.6.15", 181 | "reflect-metadata": "^0.1.13", 182 | "wc-context": "github:launchscout/wc-context" 183 | } 184 | }, 185 | "node_modules/quill-delta": { 186 | "version": "5.0.0", 187 | "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.0.0.tgz", 188 | "integrity": "sha512-lVORU8dBPJdxPmwtdGhfRcz2cekn8Osuj5kwHoPMQ3MNlDT/IZ0CGUnQ/tLsAaTn31LWcDC1KyL+gkiGbBlBBw==", 189 | "peer": true, 190 | "dependencies": { 191 | "fast-diff": "1.2.0", 192 | "lodash.clonedeep": "^4.5.0", 193 | "lodash.isequal": "^4.5.0" 194 | }, 195 | "engines": { 196 | "node": ">= 12.0.0" 197 | } 198 | }, 199 | "node_modules/react": { 200 | "version": "18.2.0", 201 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", 202 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", 203 | "dev": true, 204 | "dependencies": { 205 | "loose-envify": "^1.1.0" 206 | }, 207 | "engines": { 208 | "node": ">=0.10.0" 209 | } 210 | }, 211 | "node_modules/reflect-metadata": { 212 | "version": "0.1.13", 213 | "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", 214 | "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" 215 | }, 216 | "node_modules/rxjs": { 217 | "version": "7.8.0", 218 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", 219 | "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", 220 | "peer": true, 221 | "dependencies": { 222 | "tslib": "^2.1.0" 223 | } 224 | }, 225 | "node_modules/tslib": { 226 | "version": "2.4.1", 227 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", 228 | "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", 229 | "peer": true 230 | }, 231 | "node_modules/typescript": { 232 | "version": "4.8.4", 233 | "dev": true, 234 | "license": "Apache-2.0", 235 | "bin": { 236 | "tsc": "bin/tsc", 237 | "tsserver": "bin/tsserver" 238 | }, 239 | "engines": { 240 | "node": ">=4.2.0" 241 | } 242 | }, 243 | "node_modules/wc-context": { 244 | "version": "0.11.0", 245 | "resolved": "git+ssh://git@github.com/launchscout/wc-context.git#ef9bb50d85f7e997295ef17fecd192f0bea61a9d", 246 | "license": "MIT" 247 | } 248 | }, 249 | "dependencies": { 250 | "@lit-labs/ssr-dom-shim": { 251 | "version": "1.0.0", 252 | "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.0.0.tgz", 253 | "integrity": "sha512-ic93MBXfApIFTrup4a70M/+ddD8xdt2zxxj9sRwHQzhS9ag/syqkD8JPdTXsc1gUy2K8TTirhlCqyTEM/sifNw==" 254 | }, 255 | "@lit/reactive-element": { 256 | "version": "1.6.1", 257 | "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.1.tgz", 258 | "integrity": "sha512-va15kYZr7KZNNPZdxONGQzpUr+4sxVu7V/VG7a8mRfPPXUyhEYj5RzXCQmGrlP3tAh0L3HHm5AjBMFYRqlM9SA==", 259 | "requires": { 260 | "@lit-labs/ssr-dom-shim": "^1.0.0" 261 | } 262 | }, 263 | "@types/trusted-types": { 264 | "version": "2.0.2", 265 | "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", 266 | "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" 267 | }, 268 | "arg": { 269 | "version": "5.0.2", 270 | "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", 271 | "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" 272 | }, 273 | "fast-diff": { 274 | "version": "1.2.0", 275 | "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", 276 | "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", 277 | "peer": true 278 | }, 279 | "hyperdyperid": { 280 | "version": "1.2.0", 281 | "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", 282 | "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==" 283 | }, 284 | "js-tokens": { 285 | "version": "4.0.0", 286 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 287 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 288 | "dev": true 289 | }, 290 | "json-joy": { 291 | "version": "1.19.0", 292 | "resolved": "https://registry.npmjs.org/json-joy/-/json-joy-1.19.0.tgz", 293 | "integrity": "sha512-oILIyxHJu52hQ9BFs1yFMXVKrP2cTYOdljRiNRElHzpcSnA03sSf+VKzwqEE2hGKKL1Zvml71b1wPTAQUl8CHQ==", 294 | "requires": { 295 | "arg": "^5.0.2", 296 | "hyperdyperid": "^1.2.0" 297 | } 298 | }, 299 | "lit": { 300 | "version": "2.6.1", 301 | "resolved": "https://registry.npmjs.org/lit/-/lit-2.6.1.tgz", 302 | "integrity": "sha512-DT87LD64f8acR7uVp7kZfhLRrHkfC/N4BVzAtnw9Yg8087mbBJ//qedwdwX0kzDbxgPccWRW6mFwGbRQIxy0pw==", 303 | "requires": { 304 | "@lit/reactive-element": "^1.6.0", 305 | "lit-element": "^3.2.0", 306 | "lit-html": "^2.6.0" 307 | } 308 | }, 309 | "lit-element": { 310 | "version": "3.2.2", 311 | "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.2.2.tgz", 312 | "integrity": "sha512-6ZgxBR9KNroqKb6+htkyBwD90XGRiqKDHVrW/Eh0EZ+l+iC+u+v+w3/BA5NGi4nizAVHGYvQBHUDuSmLjPp7NQ==", 313 | "requires": { 314 | "@lit/reactive-element": "^1.3.0", 315 | "lit-html": "^2.2.0" 316 | } 317 | }, 318 | "lit-html": { 319 | "version": "2.6.1", 320 | "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.6.1.tgz", 321 | "integrity": "sha512-Z3iw+E+3KKFn9t2YKNjsXNEu/LRLI98mtH/C6lnFg7kvaqPIzPn124Yd4eT/43lyqrejpc5Wb6BHq3fdv4S8Rw==", 322 | "requires": { 323 | "@types/trusted-types": "^2.0.2" 324 | } 325 | }, 326 | "lodash.clonedeep": { 327 | "version": "4.5.0", 328 | "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", 329 | "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", 330 | "peer": true 331 | }, 332 | "lodash.isequal": { 333 | "version": "4.5.0", 334 | "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", 335 | "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", 336 | "peer": true 337 | }, 338 | "loose-envify": { 339 | "version": "1.4.0", 340 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 341 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 342 | "dev": true, 343 | "requires": { 344 | "js-tokens": "^3.0.0 || ^4.0.0" 345 | } 346 | }, 347 | "phoenix": { 348 | "version": "1.6.15", 349 | "resolved": "https://registry.npmjs.org/phoenix/-/phoenix-1.6.15.tgz", 350 | "integrity": "sha512-O6AG5jTkZOOkdd/GOSCsM4v3bzBoyRnC5bEi57KhX/Daba6FvnBRzt0nhEeRRiVQGLSxDlyb0dUe9CkYWMZd8g==" 351 | }, 352 | "phx-live-state": { 353 | "version": "0.9.0", 354 | "resolved": "https://registry.npmjs.org/phx-live-state/-/phx-live-state-0.9.0.tgz", 355 | "integrity": "sha512-X9VppKb5tpXswdRn8qnEALueRSDYT/fQRIqdYCnLn9Q7C8jkTHs2Q4QRZ9HHS42KX7cQib3CmRDo6uORPzzZJQ==", 356 | "requires": { 357 | "json-joy": "^1.18.1", 358 | "lit": "^2.2.6", 359 | "phoenix": "1.6.15", 360 | "reflect-metadata": "^0.1.13", 361 | "wc-context": "github:launchscout/wc-context" 362 | } 363 | }, 364 | "quill-delta": { 365 | "version": "5.0.0", 366 | "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.0.0.tgz", 367 | "integrity": "sha512-lVORU8dBPJdxPmwtdGhfRcz2cekn8Osuj5kwHoPMQ3MNlDT/IZ0CGUnQ/tLsAaTn31LWcDC1KyL+gkiGbBlBBw==", 368 | "peer": true, 369 | "requires": { 370 | "fast-diff": "1.2.0", 371 | "lodash.clonedeep": "^4.5.0", 372 | "lodash.isequal": "^4.5.0" 373 | } 374 | }, 375 | "react": { 376 | "version": "18.2.0", 377 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", 378 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", 379 | "dev": true, 380 | "requires": { 381 | "loose-envify": "^1.1.0" 382 | } 383 | }, 384 | "reflect-metadata": { 385 | "version": "0.1.13", 386 | "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", 387 | "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" 388 | }, 389 | "rxjs": { 390 | "version": "7.8.0", 391 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", 392 | "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", 393 | "peer": true, 394 | "requires": { 395 | "tslib": "^2.1.0" 396 | } 397 | }, 398 | "tslib": { 399 | "version": "2.4.1", 400 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", 401 | "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", 402 | "peer": true 403 | }, 404 | "typescript": { 405 | "version": "4.8.4", 406 | "dev": true 407 | }, 408 | "wc-context": { 409 | "version": "git+ssh://git@github.com/launchscout/wc-context.git#ef9bb50d85f7e997295ef17fecd192f0bea61a9d", 410 | "from": "wc-context@github:launchscout/wc-context" 411 | } 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "use-live-state", 3 | "version": "0.0.2", 4 | "description": "A react hook for LiveState", 5 | "module": "build/useLiveState.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "build": "tsc", 9 | "setup": "npm install && npm run build" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/launchscout/live_state.git" 14 | }, 15 | "keywords": [ 16 | "elixir", 17 | "typescript", 18 | "react", 19 | "phoenix", 20 | "LiveState" 21 | ], 22 | "author": "Chris Nelson", 23 | "license": "ISC", 24 | "bugs": { 25 | "url": "https://github.com/launchscout/live_state/issues" 26 | }, 27 | "homepage": "https://github.com/launchscout/live_state/tree/main/use-live-state#readme", 28 | "peerDependencies": { 29 | "react": ">= 16.8.0" 30 | }, 31 | "dependencies": { 32 | "phx-live-state": ">= 0.9.0" 33 | }, 34 | "devDependencies": { 35 | "typescript": "^4.8.4", 36 | "react": ">= 16.8.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "outDir": "build", 5 | "moduleResolution": "node", 6 | "declaration": true, 7 | "allowSyntheticDefaultImports": true, 8 | "experimentalDecorators": true 9 | }, 10 | "include": ["useLiveState.ts"] 11 | } -------------------------------------------------------------------------------- /useLiveState.ts: -------------------------------------------------------------------------------- 1 | import LiveState from 'phx-live-state'; 2 | import { useState, useEffect } from 'react'; 3 | 4 | const useLiveState = (liveState: LiveState, intialState: any) => { 5 | const [state, setState] = useState(intialState); 6 | useEffect(() => { 7 | liveState.connect(); 8 | const handleStateChange = ({detail: {state}}) => setState(state); 9 | liveState.addEventListener('livestate-change', handleStateChange); 10 | return () => { 11 | liveState.removeEventListener('livestate-change', handleStateChange); 12 | }; 13 | }); 14 | 15 | const pushEvent = (event, payload) => { 16 | liveState.pushEvent(event, payload); 17 | } 18 | 19 | return [state, pushEvent]; 20 | } 21 | 22 | export default useLiveState --------------------------------------------------------------------------------