├── .editorconfig
├── .eslintrc.js
├── .gitignore
├── .prettierrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── example
├── .env
├── .gitignore
├── README.md
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
├── src
│ ├── Counter.js
│ └── index.js
└── yarn.lock
├── package.json
├── src
├── StateInspector.tsx
├── context.ts
├── index.ts
├── useReducer.ts
└── useState.ts
├── tests
├── index.test.tsx
├── test.setup.js
└── tsconfig.json
├── tsconfig.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | trim_trailing_whitespace = true
3 | insert_final_newline = true
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | "react-app",
4 | "prettier/@typescript-eslint",
5 | "plugin:prettier/recommended"
6 | ],
7 | settings: {
8 | react: {
9 | version: "detect"
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /dist
2 | /node_modules
3 | .vscode
4 | *.log
5 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "semi": false
4 | }
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # v1.1.0
2 |
3 | Maintenance release:
4 |
5 | - Update dependencies
6 | - Move to tsdx
7 | - TypeScript strict mode
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Thomas Roch
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # reinspect
2 |
3 | > Under development
4 |
5 | Connect React state hooks (`useState` and `useReducer`) to redux dev tools.
6 |
7 | [See it live](https://7ypv9qw6j0.codesandbox.io/)
8 |
9 | 
10 |
11 | ## Why?
12 |
13 | Hooks are great, they are a joy to use to create state in components. On the other hand, with something global and centralised like Redux, we have [great dev tools](https://github.com/zalmoxisus/redux-devtools-extension).
14 |
15 | Why not both? That's exactly what this package offers: connecting `useState` and `useReducer` to redux dev tools, so you can do the following:
16 |
17 | - Inspect actions and state for each hook
18 | - Time travel through state changes in your app
19 | - Hot reloading: save your current state and re-inject it when re-rendering
20 |
21 | ## API
22 |
23 | You need [redux devtools](https://github.com/zalmoxisus/redux-devtools-extension) installed. This package provides:
24 |
25 | - `StateInspector`: a provider which will be used by `useState` and `useReducer` to connect them to a store and redux dev tools.
26 |
27 | - It accepts optionally a `name` (name of the store in dev tools) and `initialState` (if you want to start with a given state)
28 | - You can have more than one `StateInspector` in your application, hooks will report to the nearest one
29 | - Without a `StateInspector`, `useState` and `useReducer` behave normally
30 |
31 | ```js
32 | import React from "react"
33 | import { StateInspector } from "reinspect"
34 | import App from "./App"
35 |
36 | function AppWrapper() {
37 | return (
38 |
39 |
40 |
41 | )
42 | }
43 |
44 | export default AppWrapper
45 | ```
46 |
47 | - `useState(initialState, id?)`: like [useState](https://reactjs.org/docs/hooks-reference.html#usestate) but with a 2nd argument `id` (a unique ID to identify it in dev tools). If no `id` is supplied, the hook won't be connected to dev tools.
48 |
49 | ```js
50 | import React from "react"
51 | import { useState } from "reinspect"
52 |
53 | export function CounterWithUseState({ id }) {
54 | const [count, setCount] = useState(0, id)
55 |
56 | return (
57 |
58 | setCount(count - 1)}>-
59 | {count} setCount(count + 1)}>+
60 |
61 | )
62 | }
63 | ```
64 |
65 | - `useReducer(reducer, initialState, initializer?, id?)`: like [useReducer](https://reactjs.org/docs/hooks-reference.html#usereducer) but with a 4th argument `id` (a unique ID to identify it in dev tools). If no `id` is supplied, the hook won't be connected to dev tools. You can use identity function (`state => state`) as 3rd parameter to mock lazy initialization.
66 |
--------------------------------------------------------------------------------
/example/.env:
--------------------------------------------------------------------------------
1 | SKIP_PREFLIGHT_CHECK=true
2 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # Reinspect example
2 |
3 | To run:
4 |
5 | - install dependencies with yarn (`yarn`)
6 | - run the example with `yarn start`
7 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "~16.13.0",
7 | "react-dom": "~16.13.0",
8 | "react-scripts": "3.4.0",
9 | "reinspect": "file:../dist"
10 | },
11 | "scripts": {
12 | "start": "react-scripts start"
13 | },
14 | "eslintConfig": {
15 | "extends": "react-app"
16 | },
17 | "browserslist": [
18 | ">0.2%",
19 | "not dead",
20 | "not ie <= 11",
21 | "not op_mini all"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/example/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/troch/reinspect/4d0b4160afe6f51596b984ba9fde737d609ddedd/example/public/favicon.ico
--------------------------------------------------------------------------------
/example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
22 | React App
23 |
24 |
25 |
26 | You need to enable JavaScript to run this app.
27 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/example/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/example/src/Counter.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { useState } from "reinspect"
3 |
4 | export function Counter({ id }) {
5 | const [count, setCount] = useState(0, id)
6 |
7 | return (
8 |
9 | setCount(count - 1)}>- {count}{" "}
10 | setCount(count + 1)}>+
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/example/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import ReactDOM from "react-dom"
3 | import { StateInspector, useState } from "reinspect"
4 | import { Counter } from "./Counter"
5 |
6 | function App() {
7 | return (
8 |
9 |
10 |
11 | )
12 | }
13 |
14 | function Counters() {
15 | const [count, setCount] = useState(4, "count")
16 |
17 | return (
18 |
19 | {window.__REDUX_DEVTOOLS_EXTENSION__ ? (
20 |
Open redux devtools to see it in action!
21 | ) : (
22 |
23 | You need Redux dev tools:{" "}
24 |
25 | https://github.com/zalmoxisus/redux-devtools-extension
26 |
27 |
28 | )}
29 |
30 | setCount(count + 1)}>Add counter
31 |
32 | setCount(count - 1)} disabled={count === 0}>
33 | Remove counter
34 |
35 |
36 |
37 | {Array.from({ length: count }).map((_, index) => (
38 |
39 | ))}
40 |
41 | )
42 | }
43 |
44 | ReactDOM.render( , document.getElementById("root"))
45 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reinspect",
3 | "version": "1.1.0",
4 | "description": "Use redux devtools to inspect useState and useReducer hooks",
5 | "files": [
6 | "dist"
7 | ],
8 | "main": "dist/index.js",
9 | "module": "dist/reinspect.esm.js",
10 | "typings": "dist/index.d.ts",
11 | "repository": "git@github.com:troch/reinspect.git",
12 | "author": "Thomas Roch ",
13 | "license": "MIT",
14 | "scripts": {
15 | "start": "tsdx watch",
16 | "build": "tsdx build",
17 | "test": "tsdx test",
18 | "lint": "tsdx lint src tests"
19 | },
20 | "devDependencies": {
21 | "tsdx": "0.12.3",
22 | "@types/enzyme": "~3.9.1",
23 | "@types/enzyme-adapter-react-16": "~1.0.5",
24 | "@types/jest": "~24.0.11",
25 | "@types/react": "~16.8.3",
26 | "conventional-changelog-cli": "~2.0.12",
27 | "enzyme": "~3.9.0",
28 | "enzyme-adapter-react-16": "~1.12.1",
29 | "husky": "^4.2.3",
30 | "react": "~16.8.6",
31 | "react-dom": "~16.8.6",
32 | "react-test-renderer": "~16.8.2",
33 | "typescript": "~3.8.3"
34 | },
35 | "dependencies": {
36 | "redux": "^4.0.5"
37 | },
38 | "peerDependencies": {
39 | "react": ">=16.8.1"
40 | },
41 | "jest": {
42 | "preset": "ts-jest",
43 | "testEnvironment": "jsdom",
44 | "setupFiles": [
45 | "/tests/test.setup.js"
46 | ],
47 | "globals": {
48 | "ts-jest": {
49 | "tsConfig": "/tests/tsconfig.json"
50 | }
51 | }
52 | },
53 | "husky": {
54 | "hooks": {
55 | "pre-commit": "tsdx lint src tests"
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/StateInspector.tsx:
--------------------------------------------------------------------------------
1 | import React, { ReducerAction, Reducer, useMemo, useEffect } from "react"
2 | import { createStore } from "redux"
3 | import { EnhancedStore, StateInspectorContext } from "./context"
4 |
5 | declare global {
6 | interface Window {
7 | __REDUX_DEVTOOLS_EXTENSION__?: any
8 | }
9 | }
10 |
11 | interface StateInspectorProps {
12 | name?: string
13 | initialState?: any
14 | }
15 |
16 | interface StoreReducerAction {
17 | type: string
18 | payload: any
19 | }
20 |
21 | const omit = (obj: Record, keyToRemove: string) =>
22 | Object.keys(obj)
23 | .filter(key => key !== keyToRemove)
24 | .reduce>((acc, key) => {
25 | acc[key] = obj[key]
26 |
27 | return acc
28 | }, {})
29 |
30 | export const StateInspector: React.FC = ({
31 | name,
32 | initialState = {},
33 | children
34 | }) => {
35 | const store = useMemo(() => {
36 | if (typeof window === "undefined" || !window.__REDUX_DEVTOOLS_EXTENSION__) {
37 | return undefined
38 | }
39 |
40 | const registeredReducers: Record<
41 | string | number,
42 | Reducer>
43 | > = {}
44 |
45 | const storeReducer: Reducer = (state, action) => {
46 | const actionReducerId = action.type.split("/")[0]
47 | const isInitAction = /\/_init$/.test(action.type)
48 | const isTeardownAction = /\/_teardown$/.test(action.type)
49 |
50 | const currentState = isTeardownAction
51 | ? omit(state, actionReducerId)
52 | : { ...state }
53 |
54 | return Object.keys(registeredReducers).reduce((acc, reducerId) => {
55 | const reducer = registeredReducers[reducerId]
56 | const reducerState = state[reducerId]
57 | const reducerAction = action.payload
58 | const isForCurrentReducer = actionReducerId === reducerId
59 |
60 | if (isForCurrentReducer) {
61 | acc[reducerId] = isInitAction
62 | ? action.payload
63 | : reducer(reducerState, reducerAction)
64 | } else {
65 | acc[reducerId] = reducerState
66 | }
67 |
68 | return acc
69 | }, currentState)
70 | }
71 |
72 | const store: EnhancedStore = createStore(
73 | storeReducer,
74 | initialState,
75 | window.__REDUX_DEVTOOLS_EXTENSION__({
76 | name: name || "React state",
77 | actionsBlacklist: ["/_init", "/_teardown"]
78 | })
79 | )
80 |
81 | store.registerHookedReducer = (reducer, initialState, reducerId) => {
82 | registeredReducers[reducerId] = reducer
83 |
84 | store.dispatch({
85 | type: `${reducerId}/_init`,
86 | payload: initialState
87 | })
88 |
89 | return () => {
90 | delete registeredReducers[reducerId]
91 |
92 | store.dispatch({
93 | type: `${reducerId}/_teardown`
94 | })
95 | }
96 | }
97 |
98 | return store
99 | }, [])
100 |
101 | useEffect(() => {
102 | store && store.dispatch({ type: "REINSPECT/@@INIT", payload: {} })
103 | }, [])
104 |
105 | return (
106 |
107 | {children}
108 |
109 | )
110 | }
111 |
--------------------------------------------------------------------------------
/src/context.ts:
--------------------------------------------------------------------------------
1 | import React, { Reducer } from "react"
2 | import { Store } from "redux"
3 |
4 | type UnsubscribeFn = () => void
5 |
6 | export type EnhancedStore = Store & {
7 | registerHookedReducer: (
8 | reducer: Reducer,
9 | initialState: any,
10 | reducerId: string | number
11 | ) => UnsubscribeFn
12 | }
13 |
14 | export const StateInspectorContext = React.createContext<
15 | EnhancedStore | undefined
16 | >(undefined)
17 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export { StateInspector } from "./StateInspector"
2 | export { useReducer } from "./useReducer"
3 | export { useState } from "./useState"
4 |
--------------------------------------------------------------------------------
/src/useReducer.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-hooks/rules-of-hooks */
2 | import {
3 | useReducer as useReactReducer,
4 | Reducer,
5 | useMemo,
6 | Dispatch,
7 | useState,
8 | useEffect,
9 | useContext,
10 | ReducerState,
11 | ReducerAction
12 | } from "react"
13 | import { StateInspectorContext, EnhancedStore } from "./context"
14 |
15 | export function useHookedReducer(
16 | reducer: Reducer,
17 | initialState: S,
18 | store: EnhancedStore,
19 | reducerId: string | number
20 | ): [S, Dispatch] {
21 | const initialReducerState = useMemo(() => {
22 | const initialStateInStore = store.getState()[reducerId]
23 | return initialStateInStore === undefined
24 | ? initialState
25 | : initialStateInStore
26 | }, [])
27 |
28 | const [localState, setState] = useState(initialReducerState)
29 |
30 | const dispatch = useMemo>(() => {
31 | const dispatch = (action: any) => {
32 | if (
33 | action &&
34 | typeof action === "object" &&
35 | typeof action.type === "string"
36 | ) {
37 | store.dispatch({
38 | type: `${reducerId}/${action.type}`,
39 | payload: action
40 | })
41 | } else {
42 | store.dispatch({
43 | type: reducerId,
44 | payload: action
45 | })
46 | }
47 | }
48 |
49 | return dispatch
50 | }, [])
51 |
52 | useEffect(() => {
53 | const teardown = store.registerHookedReducer(
54 | reducer,
55 | initialReducerState,
56 | reducerId
57 | )
58 |
59 | let lastReducerState = localState
60 | const unsubscribe = store.subscribe(() => {
61 | const storeState: any = store.getState()
62 | const reducerState = storeState[reducerId]
63 |
64 | if (lastReducerState !== reducerState) {
65 | setState(reducerState)
66 | }
67 |
68 | lastReducerState = reducerState
69 | })
70 |
71 | return () => {
72 | unsubscribe()
73 | teardown()
74 | }
75 | }, [])
76 |
77 | return [localState, dispatch]
78 | }
79 |
80 | export function useReducer>(
81 | reducer: R,
82 | initialState: ReducerState,
83 | id?: string | number
84 | ): [ReducerState, Dispatch>]
85 | export function useReducer, I>(
86 | reducer: R,
87 | initialState: I,
88 | initializer: (arg: I) => ReducerState,
89 | id?: string | number
90 | ): [ReducerState, Dispatch>]
91 | export function useReducer, I>(
92 | reducer: R,
93 | initialState: I & ReducerState,
94 | initializer: (arg: I & ReducerState) => ReducerState,
95 | id?: string | number
96 | ): [ReducerState, Dispatch>]
97 | export function useReducer, I>(
98 | reducer: R,
99 | initialState: I & ReducerState,
100 | ...args: any[]
101 | ) {
102 | let id: string | number | undefined
103 | let initializer: (arg: I | (I & ReducerState)) => ReducerState = args[0]
104 |
105 | if (args.length === 2) {
106 | initializer = args[0]
107 | id = args[1]
108 | } else if (typeof args[0] === "string" || typeof args[0] === "number") {
109 | id = args[0]
110 | } else {
111 | initializer = args[0]
112 | id = args[1]
113 | }
114 |
115 | const store = useContext(StateInspectorContext)
116 |
117 | const initializedState = initializer
118 | ? initializer(initialState)
119 | : initialState
120 |
121 | return store && id
122 | ? useHookedReducer(reducer, initializedState, store, id)
123 | : useReactReducer(reducer, initialState, initializer)
124 | }
125 |
--------------------------------------------------------------------------------
/src/useState.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable react-hooks/rules-of-hooks */
2 | import { useHookedReducer } from "./useReducer"
3 | import { useMemo, useContext, useState as useReactState } from "react"
4 | import { EnhancedStore, StateInspectorContext } from "./context"
5 |
6 | type StateAction = S | ((s: S) => S)
7 |
8 | function stateReducer(state: S, action: StateAction): S {
9 | return typeof action === "function" ? (action as (s: S) => S)(state) : action
10 | }
11 |
12 | export const useState = (
13 | initialState: S | (() => S),
14 | id: string | number
15 | ) => {
16 | const inspectorStore = useContext(StateInspectorContext)
17 | // Keeping the first values
18 | const [store, reducerId] = useMemo<
19 | [EnhancedStore | undefined, string | number]
20 | >(() => [inspectorStore, id], [])
21 |
22 | if (!store || !reducerId) {
23 | return useReactState(initialState)
24 | }
25 |
26 | const finalInitialState = useMemo(
27 | () =>
28 | typeof initialState === "function"
29 | ? (initialState as () => S)()
30 | : initialState,
31 | []
32 | )
33 |
34 | return useHookedReducer(
35 | stateReducer,
36 | finalInitialState,
37 | store,
38 | reducerId
39 | )
40 | }
41 |
--------------------------------------------------------------------------------
/tests/index.test.tsx:
--------------------------------------------------------------------------------
1 | import React, { useReducer as useReactReducer } from "react"
2 | import { StateInspector, useReducer as useReinspectReducer } from "../src"
3 | import { shallow, mount } from "enzyme"
4 |
5 | const Wrapper: React.FC = ({ children }) => {
6 | return (
7 |
8 |
9 | {children}
10 |
11 | )
12 | }
13 |
14 | const initArg = {
15 | start: 3
16 | }
17 |
18 | const init = (arg: typeof initArg) => ({
19 | count: arg.start
20 | })
21 |
22 | type State = ReturnType
23 |
24 | const INCREMENT = "INCREMENT"
25 |
26 | type Actions = {
27 | type: typeof INCREMENT
28 | }
29 |
30 | const myReducer = (state: State, action: Actions) => {
31 | switch (action.type) {
32 | case INCREMENT:
33 | return { ...state, count: state.count + 1 }
34 | default:
35 | return state
36 | }
37 | }
38 |
39 | describe("reinspect", () => {
40 | it("render StateInspector", () => {
41 | const result = shallow( )
42 | .find({ name: "Test" })
43 | .exists()
44 |
45 | expect(result).toBeTruthy()
46 | })
47 |
48 | it("react useReducer", () => {
49 | const Counter: React.FC = () => {
50 | const [state, dispatch] = useReactReducer(myReducer, initArg, init)
51 |
52 | const handleClick = () => {
53 | dispatch({ type: INCREMENT })
54 | }
55 |
56 | return (
57 |
58 | Count: {state.count}
59 |
60 | Increment
61 |
62 |
63 | )
64 | }
65 |
66 | const counter = mount(
67 |
68 |
69 |
70 | )
71 |
72 | expect(counter.find(".display").text()).toBe("Count: 3")
73 | counter.find(".increment").simulate("click")
74 | expect(counter.find(".display").text()).toBe("Count: 4")
75 | })
76 |
77 | it("reinspect useReducer", () => {
78 | const Counter: React.FC = () => {
79 | const [state, dispatch] = useReinspectReducer(
80 | myReducer,
81 | initArg,
82 | init,
83 | "NAME"
84 | )
85 |
86 | const handleClick = () => {
87 | dispatch({ type: INCREMENT })
88 | }
89 |
90 | return (
91 |
92 | Count: {state.count}
93 |
94 | Increment
95 |
96 |
97 | )
98 | }
99 |
100 | const counter = mount(
101 |
102 |
103 |
104 | )
105 |
106 | expect(counter.find(".display").text()).toBe("Count: 3")
107 | counter.find(".increment").simulate("click")
108 | expect(counter.find(".display").text()).toBe("Count: 4")
109 | })
110 |
111 | it("reinspect useReducer type overloads", () => {
112 | const Counter: React.FC = () => {
113 | type MyState = {
114 | start: number
115 | count: number
116 | }
117 |
118 | const initArg: Partial = { start: 2 }
119 |
120 | const inti = ({ start }: typeof initArg) => ({
121 | start,
122 | count: start ? start : 0 + 1
123 | })
124 |
125 | // inital arg as partial of state
126 | const [state, dispatch] = useReinspectReducer(
127 | myReducer,
128 | initArg,
129 | inti,
130 | "NAME"
131 | )
132 |
133 | // any init arg
134 | const [state2, dispatch2] = useReinspectReducer(
135 | myReducer,
136 | {
137 | color: "black"
138 | },
139 | ({ color }) => ({ count: 123 }),
140 | "NAME"
141 | )
142 |
143 | // inital state only
144 | const [state3, dispatch3] = useReinspectReducer(
145 | myReducer,
146 | { count: 2 },
147 | "NAME"
148 | )
149 |
150 | return (
151 |
154 | )
155 | }
156 |
157 | const counter = shallow(
158 |
159 |
160 |
161 | )
162 |
163 | expect(counter.exists()).toBeTruthy()
164 | })
165 | })
166 |
--------------------------------------------------------------------------------
/tests/test.setup.js:
--------------------------------------------------------------------------------
1 | const Enzyme = require("enzyme")
2 | const Adapter = require("enzyme-adapter-react-16")
3 |
4 | Enzyme.configure({ adapter: new Adapter() })
5 |
--------------------------------------------------------------------------------
/tests/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "module": "commonjs"
5 | // "strict": true,
6 | },
7 | "files": ["./index.test.tsx"]
8 | }
9 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "allowSyntheticDefaultImports": true,
5 | "esModuleInterop": true,
6 | "jsx": "react",
7 | "lib": ["es2015", "dom"],
8 | "declaration": true,
9 | "declarationDir": "types",
10 | "strict": true,
11 | "allowJs": true
12 | },
13 | "exclude": ["node_modules"],
14 | "include": ["src"]
15 | }
16 |
--------------------------------------------------------------------------------