├── .gitignore
├── LICENSE
├── README.md
├── chapter01
├── 01_usecount.js
├── 02_globalstate.js
├── 03_usestate.js
├── 04_usereducer.js
├── 05_usestate_with_usereducer.js
├── 06_usereducer_with_usestate.js
├── 07_usereducer_init.js
└── 08_usereducer_inline.js
├── chapter02
├── 01_localstates.js
├── 02_lift_state_up.js
├── 03_lift_content_up.js
└── 04_globalstate.js
├── chapter03
├── 01_using-usestate-without-usecontext
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 02_using-usecontext-with-static-value
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 03_using-usestate-with-usecontext
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 04_how-context-propagation-works
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 05_pitfall-when-using-context-for-object
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 06_creating-small-state-pieces
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 07_creating-one-state-with-userreducer-and-propagate-with-multiple-contexts
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 08_creating-custom-hook-and-provider-component
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 09_factory-pattern-with-custom-hook
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
└── 10_avoiding-provider-nesting-reduceright
│ ├── package.json
│ ├── public
│ └── index.html
│ ├── src
│ ├── App.tsx
│ ├── index.tsx
│ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── chapter04
├── 01_naive_solution_to_module_state
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 02_use_store_with_subscription
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 03_use_store_with_selector
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
└── 04_use_subscription_with_store
│ ├── package.json
│ ├── public
│ └── index.html
│ ├── src
│ ├── App.tsx
│ ├── index.tsx
│ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── chapter05
└── 01_combine_context_and_subscription
│ ├── package.json
│ ├── public
│ └── index.html
│ ├── src
│ ├── App.tsx
│ ├── index.tsx
│ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── chapter07
├── 01_counter_example
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
└── 02_todo_example
│ ├── package.json
│ ├── public
│ └── index.html
│ ├── src
│ ├── App.tsx
│ ├── index.tsx
│ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── chapter08
├── 01_comparison
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── AppWithContext.tsx
│ │ ├── AppWithJotai.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 02_counter
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 03_provider
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 04_todo_app_single_atom
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
└── 05_todo_app_atoms_in_atom
│ ├── package.json
│ ├── public
│ └── index.html
│ ├── src
│ ├── App.tsx
│ ├── index.tsx
│ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── chapter09
├── 01_counter
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 02_todo_app
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
└── 03_another_todo_app
│ ├── package.json
│ ├── public
│ └── index.html
│ ├── src
│ ├── App.tsx
│ ├── index.tsx
│ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── chapter10
├── 01_bare_context
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 02_with_usestate
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 03_with_usereducer
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
└── 04_with_reactredux
│ ├── package.json
│ ├── public
│ └── index.html
│ ├── src
│ ├── App.tsx
│ ├── index.tsx
│ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── chapter11
├── 01_redux_counter
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── app
│ │ │ └── store.ts
│ │ ├── features
│ │ │ └── counter
│ │ │ │ ├── Counter.tsx
│ │ │ │ └── counterSlice.ts
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 02_zustand_counter
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── Counter.tsx
│ │ ├── index.tsx
│ │ ├── react-app-env.d.ts
│ │ └── store.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 03_recoil_charcounter
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 04_jotai_charcounter
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── 05_mobx_timer
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── App.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
└── 06_valtio_timer
│ ├── package.json
│ ├── public
│ └── index.html
│ ├── src
│ ├── App.tsx
│ ├── index.tsx
│ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── yarn.lock
└── cover.jpg
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.swp
3 | node_modules
4 | build
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Packt
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 | 
2 |
3 | # 리액트 훅을 활용한 마이크로 상태 관리
4 | ### 리액트 상태 관리의 기본 개념부터 동작 원리와 문제 해결, 렌더링 최적화 기법까지
5 |
6 | - **다이시 카토** 지음 | **이선협, 김지은** 옮김
7 | - ISBN: 9791158394899
8 | - 판형: 175\*235\*14mm
9 | - 27,000원 | 2024년 2월 20일 발행 | 260쪽
10 | - [책 홈페이지](https://wikibook.co.kr/msmrh/)
11 | - [도서 미리보기](http://www.yes24.com/Product/Viewer/Preview/124899726)
12 | - [도서 관련 문의](https://wikibook.co.kr/support/contact/)
13 |
14 | ---
15 |
16 | 이 책에서는 다양한 상태 관리 방법과 유명한 상태 관리 라이브러리인 Zustand, Jotai, Valtio, React Tracked의 사용법을 소개한다. 또한 실무에서 유용하게 활용할 수 있는 여러 사용 사례에 대한 패턴과 리렌더링 최적화에 대한 내용을 다룬다.
17 |
18 | 이 책을 처음부터 끝까지 정독하면 리액트에서 상태를 관리하는 방법과 원리를 비롯해 애플리케이션 요구사항에 적합한 상태 관리 라이브러리를 선택할 수 있을 것이다.
19 |
20 | **★ 이 책에서 다루는 내용 ★**
21 |
22 | - 마이크로 상태 관리의 개념과 구현
23 | - 지역 상태와 전역 상태의 개념과 구현
24 | - 리액트 컨텍스트를 통한 전역 상태 관리
25 | - 모듈 상태를 통한 전역 상태 관리
26 | - 리렌더링 최적화
27 | - Zustand, Jotai, Valtio, React Tracked의 사용법과 동작 원리
28 | - 여러 상태 관리 라이브러리의 장단점 비교
29 | - 요구사항에 적합한 라이브러리 선택 방법
30 |
31 | ---
32 |
33 | ## 구입처
34 |
35 | - [예스24](https://www.yes24.com/Product/Goods/124899726)
36 | - [교보문고](https://product.kyobobook.co.kr/detail/S000212233308)
37 | - [인터파크](https://book.interpark.com/product/BookDisplay.do?_method=detail&sc.prdNo=356868972)
38 | - [알라딘](https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=333599370)
--------------------------------------------------------------------------------
/chapter01/01_usecount.js:
--------------------------------------------------------------------------------
1 | const Component = () => {
2 | const [count, setCount] = useState(0);
3 | return (
4 |
5 | {count}
6 |
7 |
8 | );
9 | };
10 |
11 | const useCount = () => {
12 | const [count, setCount] = useState(0);
13 | return [count, setCount];
14 | };
15 |
16 | const Component = () => {
17 | const [count, setCount] = useCount();
18 | return (
19 |
20 | {count}
21 |
22 |
23 | );
24 | };
25 |
26 | const useCount = () => {
27 | const [count, setCount] = useState(0);
28 | useEffect(() => {
29 | console.log('count is changed to', count);
30 | }, [count]);
31 | return [count, setCount];
32 | };
33 |
34 | const useCount = () => {
35 | const [count, setCount] = useState(0);
36 | const inc = () => setCount((c) => c + 1);
37 | return [count, inc];
38 | };
39 |
--------------------------------------------------------------------------------
/chapter01/02_globalstate.js:
--------------------------------------------------------------------------------
1 | const Component = () => {
2 | const [state, setState] = useState();
3 | return (
4 |
5 | {JSON.stringify(state)}
6 |
7 |
8 | );
9 | };
10 |
11 | const Child = ({ state, setState }) => {
12 | const setFoo = () => setState(
13 | (prev) => ({ ...prev, foo: ‘foo’ })
14 | );
15 | return (
16 |
17 | {JSON.stringify(state)}
18 |
19 |
20 | );
21 | };
22 |
23 | const Component1 = () => {
24 | const [state, setState] = useGlobalState();
25 | return (
26 |
27 | {JSON.stringify(state)}
28 |
29 | );
30 | };
31 |
32 | const Component2 = () => {
33 | const [state, setState] = useGlobalState();
34 | return (
35 |
36 | {JSON.stringify(state)}
37 |
38 | );
39 | };
40 |
--------------------------------------------------------------------------------
/chapter01/03_usestate.js:
--------------------------------------------------------------------------------
1 | const Component = () => {
2 | const [count, setCount] = useState(0);
3 | return (
4 |
5 | {count}
6 |
7 |
8 | );
9 | };
10 |
11 | const Component = () => {
12 | const [state, setState] = useState({ count: 0 });
13 | return (
14 |
15 | {state.count}
16 |
19 |
20 | );
21 | };
22 |
23 | const Component = () => {
24 | const [state, setState] = useState({ count: 0 });
25 | return (
26 |
27 | {state.count}
28 |
33 |
34 | );
35 | };
36 |
37 | const Component = () => {
38 | const [count, setCount] = useState(0);
39 | return (
40 |
41 | {count}
42 |
45 |
46 | );
47 | };
48 |
49 | const Component = () => {
50 | const [count, setCount] = useState(0);
51 | return (
52 |
53 | {count}
54 |
57 |
58 | );
59 | };
60 |
61 | const Component = () => {
62 | const [count, setCount] = useState(0);
63 | useEffect(() => {
64 | const id = setInterval(() => setCount((c) => c + 1), 1000);
65 | return () => clearInterval(id);
66 | }, []);
67 | return (
68 |
69 | {count}
70 |
76 |
77 | );
78 | };
79 |
80 | const init = () => 0;
81 |
82 | const Component = () => {
83 | const [count, setCount] = useState(init);
84 | return (
85 |
86 | {count}
87 |
90 |
91 | );
92 | };
93 |
--------------------------------------------------------------------------------
/chapter01/04_usereducer.js:
--------------------------------------------------------------------------------
1 | const reducer = (state, action) => {
2 | switch (action.type) {
3 | case 'INCREMENT':
4 | return { ...state, count: state.count + 1 };
5 | case 'SET_TEXT':
6 | return { ...state, text: action.text };
7 | default:
8 | throw new Error('unknown action type');
9 | }
10 | };
11 |
12 | const Component = () => {
13 | const [state, dispatch] = useReducer(
14 | reducer,
15 | { count: 0, text: 'hi' },
16 | );
17 | return (
18 |
19 | {state.count}
20 |
25 |
28 | dispatch({ type: 'SET_TEXT', text: e.target.value })}
29 | />
30 |
31 | );
32 | };
33 |
34 | const reducer = (state, action) => {
35 | switch (action.type) {
36 | case 'INCREMENT':
37 | return { ...state, count: state.count + 1 };
38 | case 'SET_TEXT':
39 | if (!action.text) {
40 | // bail out
41 | return state
42 | }
43 | return { ...state, text: action.text };
44 | default:
45 | throw new Error('unknown action type');
46 | }
47 | };
48 |
49 | const reducer = (count, delta) => {
50 | if (delta < 0) {
51 | throw new Error('delta cannot be negative');
52 | }
53 | if (delta > 10) {
54 | // too big, just ignore
55 | return count
56 | }
57 | if (count < 100) {
58 | // add bonus
59 | return count + delta + 10
60 | }
61 | return count + delta
62 | }
63 |
64 | const init = (count) => ({ count, text: 'hi' });
65 |
66 | const reducer = (state, action) => {
67 | switch (action.type) {
68 | case 'INCREMENT':
69 | return { ...state, count: state.count + 1 };
70 | case 'SET_TEXT':
71 | return { ...state, text: action.text };
72 | default:
73 | throw new Error('unknown action type');
74 | }
75 | };
76 |
77 | const Component = () => {
78 | const [state, dispatch] = useReducer(reducer, 0, init);
79 | return (
80 |
81 | {state.count}
82 |
87 |
90 | dispatch({ type: 'SET_TEXT', text: e.target.value })}
91 | />
92 |
93 | );
94 | };
95 |
--------------------------------------------------------------------------------
/chapter01/05_usestate_with_usereducer.js:
--------------------------------------------------------------------------------
1 | const useState = (initialState) => {
2 | const [state, dispatch] = useReducer(
3 | (prev, action) =>
4 | typeof action === 'function' ? action(prev) : action,
5 | initialState,
6 | );
7 | return [state, dispatch];
8 | };
9 |
10 | const reducer = (prev, action) =>
11 | typeof action === 'function' ? action(prev): action;
12 |
13 | const useState = (initialState) =>
14 | useReducer(reducer, initialState);
15 |
--------------------------------------------------------------------------------
/chapter01/06_usereducer_with_usestate.js:
--------------------------------------------------------------------------------
1 | const useReducer = (reducer, initialState) => {
2 | const [state, setState] = useState(initialState);
3 | const dispatch = (action) =>
4 | setState(prev => reducer(prev, action));
5 | return [state, dispatch];
6 | };
7 |
8 | const useReducer = (reducer, initialArg, init) => {
9 | const [state, setState] = useState(
10 | init ? () => init(initialArg) : initialArg,
11 | );
12 | const dispatch = useCallback(
13 | (action) => setState(prev => reducer(prev, action)),
14 | [reducer],
15 | );
16 | return [state, dispatch];
17 | };
18 |
--------------------------------------------------------------------------------
/chapter01/07_usereducer_init.js:
--------------------------------------------------------------------------------
1 | const init = (count) => ({ count })
2 |
3 | const reducer = (prev, delta) => ({ ...prev, count: prev.count + delta })
4 |
5 | const ComponentWithUseReducer = ({ initialCount }) => {
6 | const [state, dispatch] = useReducer(
7 | reducer,
8 | initialCount,
9 | init,
10 | );
11 | return (
12 |
13 | {state.count}
14 |
15 |
16 | );
17 | };
18 |
19 | const ComponentWithUseState = ({ initialCount }) => {
20 | const [state, setState] = useState(() => init(initialCount));
21 | const dispatch = (delta) =>
22 | setState((prev) => reducer(prev, delta));
23 | return (
24 |
25 | {state.count}
26 |
27 |
28 | );
29 | };
30 |
--------------------------------------------------------------------------------
/chapter01/08_usereducer_inline.js:
--------------------------------------------------------------------------------
1 | const useScore = (bonus) =>
2 | useReducer((prev, delta) => prev + delta + bonus, 0)
3 |
--------------------------------------------------------------------------------
/chapter02/01_localstates.js:
--------------------------------------------------------------------------------
1 | const addOne = (n) => n + 1;
2 |
3 | let base = 1;
4 |
5 | const addBase = (n) => n + base;
6 |
7 | const createContainer = () => {
8 | let base = 1;
9 | const addBase = (n) => n + base;
10 | const changeBase = (b) => { base = b; };
11 | return { addBase, changeBase };
12 | };
13 |
14 | const { addBase, changeBase } = createContainer();
15 |
16 | const Component = ({ number }) => {
17 | return {number}
;
18 | };
19 |
20 | const AddOne = ({ number }) => {
21 | return {number + 1}
;
22 | };
23 |
24 | const AddBase = ({ number }) => {
25 | const [base, changeBase] = useState(1);
26 | return {number + base}
;
27 | };
28 |
--------------------------------------------------------------------------------
/chapter02/02_lift_state_up.js:
--------------------------------------------------------------------------------
1 | const Component1 = () => {
2 | const [count, setCount] = useState(0);
3 | return (
4 |
5 | {count}
6 |
9 |
10 | );
11 | };
12 |
13 | const Component2 = () => {
14 | const [count, setCount] = useState(0);
15 | return (
16 |
17 | {count}
18 |
21 |
22 | );
23 | };
24 |
25 | const Component1 = ({ count, setCount }) => {
26 | return (
27 |
28 | {count}
29 |
32 |
33 | );
34 | };
35 |
36 | const Component2 = ({ count, setCount }) => {
37 | return (
38 |
39 | {count}
40 |
43 |
44 | );
45 | };
46 |
47 | const Parent = () => {
48 | const [count, setCount] = useState(0);
49 | return (
50 | <>
51 |
52 |
53 | >
54 | );
55 | };
56 |
--------------------------------------------------------------------------------
/chapter02/03_lift_content_up.js:
--------------------------------------------------------------------------------
1 | const AdditionalInfo = () => {
2 | return Some information
3 | };
4 |
5 | const Component1 = ({ count, setCount }) => {
6 | return (
7 |
8 | {count}
9 |
12 |
13 |
14 | );
15 | };
16 |
17 | const Parent = () => {
18 | const [count, setCount] = useState(0);
19 | return (
20 | <>
21 |
22 |
23 | >
24 | );
25 | };
26 |
27 | const AdditionalInfo = () => {
28 | return Some information
29 | };
30 |
31 | const Component1 = ({ count, setCount, additionalInfo }) => {
32 | return (
33 |
34 | {count}
35 |
38 | {additionalInfo}
39 |
40 | );
41 | };
42 |
43 | const Parent = ({ additionalInfo }) => {
44 | const [count, setCount] = useState(0);
45 | return (
46 | <>
47 |
52 |
53 | >
54 | );
55 | };
56 |
57 | const GrandParent = () => {
58 | return } />;
59 | };
60 |
61 | const AdditionalInfo = () => {
62 | return Some information
63 | };
64 |
65 | const Component1 = ({ count, setCount, children }) => {
66 | return (
67 |
68 | {count}
69 |
72 | {children}
73 |
74 | );
75 | };
76 |
77 | const Parent = ({ children }) => {
78 | const [count, setCount] = useState(0);
79 | return (
80 | <>
81 |
82 | {children}
83 |
84 |
85 | >
86 | );
87 | };
88 |
89 | const GrandParent = () => {
90 | return (
91 |
92 |
93 |
94 | );
95 | };
96 |
--------------------------------------------------------------------------------
/chapter02/04_globalstate.js:
--------------------------------------------------------------------------------
1 | const createContainer = () => {
2 | let base = 1;
3 | const addBase = (n) => n + base;
4 | const changeBase = (b) => { base = b; };
5 | return { addBase, changeBase };
6 | };
7 |
8 | const container1 = createContainer();
9 | const container2 = createContainer();
10 |
11 | container1.changeBase(10);
12 |
13 | console.log(container1.addBase(2)); // shows "12"
14 | console.log(container2.addBase(2)); // shows "3"
15 |
16 | const Component1 = ({ count, setCount }) => {
17 | return (
18 |
19 | {count}
20 |
23 |
24 | );
25 | };
26 |
27 | const Parent = ({ count, setCount }) => {
28 | return (
29 | <>
30 |
31 | >
32 | );
33 | };
34 |
35 | const GrandParent = ({ count, setCount }) => {
36 | return (
37 | <>
38 |
39 | >
40 | );
41 | };
42 |
43 | const Root = () => {
44 | const [count, setCount] = useState(0);
45 | return (
46 | <>
47 |
48 | >
49 | );
50 | };
51 |
52 | const Component1 = () => {
53 | // useGlobalCountState is a pseudo hook
54 | const [count, setCount] = useGlobalCountState();
55 | return (
56 |
57 | {count}
58 |
61 |
62 | );
63 | };
64 |
65 | const Parent = () => {
66 | return (
67 | <>
68 |
69 | >
70 | );
71 | };
72 |
73 | const GrandParent = () => {
74 | return (
75 | <>
76 |
77 | >
78 | );
79 | };
80 |
81 | const Root = () => {
82 | return (
83 | <>
84 |
85 | >
86 | );
87 | };
88 |
89 | const globalState = {
90 | authInfo: { name: 'React' },
91 | };
92 |
93 | const Component1 = () => {
94 | // useGlobalState is a pseudo hook
95 | const { authInfo } = useGlobalState();
96 | return (
97 |
98 | {authInfo.name}
99 |
100 | );
101 | };
102 |
--------------------------------------------------------------------------------
/chapter03/01_using-usestate-without-usecontext/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "web-vitals": "^1.0.1"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/chapter03/01_using-usestate-without-usecontext/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter03/01_using-usestate-without-usecontext/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Dispatch, SetStateAction, useState } from "react";
2 |
3 | const App = () => {
4 | const [count, setCount] = useState(0);
5 | return ;
6 | };
7 |
8 | const Parent = ({
9 | count,
10 | setCount,
11 | }: {
12 | count: number;
13 | setCount: Dispatch>;
14 | }) => (
15 | <>
16 |
17 |
18 | >
19 | );
20 |
21 | const Component1 = ({
22 | count,
23 | setCount,
24 | }: {
25 | count: number;
26 | setCount: Dispatch>;
27 | }) => (
28 |
29 | {count}
30 |
31 |
32 | );
33 |
34 | const Component2 = ({
35 | count,
36 | setCount,
37 | }: {
38 | count: number;
39 | setCount: Dispatch>;
40 | }) => (
41 |
42 | {count}
43 |
44 |
45 | );
46 |
47 | export default App;
48 |
--------------------------------------------------------------------------------
/chapter03/01_using-usestate-without-usecontext/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter03/01_using-usestate-without-usecontext/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter03/01_using-usestate-without-usecontext/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter03/02_using-usecontext-with-static-value/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "web-vitals": "^1.0.1"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/chapter03/02_using-usecontext-with-static-value/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter03/02_using-usecontext-with-static-value/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { createContext, useContext } from "react";
2 |
3 | const ColorContext = createContext('black');
4 |
5 | const Component = () => {
6 | const color = useContext(ColorContext);
7 | return Hello {color}
;
8 | };
9 |
10 | const App = () => (
11 | <>
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | >
26 | );
27 |
28 | export default App;
29 |
--------------------------------------------------------------------------------
/chapter03/02_using-usecontext-with-static-value/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter03/02_using-usecontext-with-static-value/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter03/02_using-usecontext-with-static-value/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter03/03_using-usestate-with-usecontext/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "web-vitals": "^1.0.1"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/chapter03/03_using-usestate-with-usecontext/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter03/03_using-usestate-with-usecontext/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { SetStateAction, createContext, useContext, useState } from "react";
2 |
3 | const CountStateContext = createContext({ count: 0, setCount: (_: SetStateAction) => {} });
4 |
5 | const App = () => {
6 | const [count, setCount] = useState(0);
7 | return (
8 |
9 |
10 |
11 | );
12 | };
13 |
14 | const Parent = () => (
15 | <>
16 |
17 |
18 | >
19 | );
20 |
21 | const Component1 = () => {
22 | const { count, setCount } = useContext(CountStateContext);
23 | return (
24 |
25 | {count}
26 |
27 |
28 | );
29 | };
30 |
31 | const Component2 = () => {
32 | const { count, setCount } = useContext(CountStateContext);
33 | return (
34 |
35 | {count}
36 |
37 |
38 | );
39 | };
40 |
41 | export default App;
42 |
--------------------------------------------------------------------------------
/chapter03/03_using-usestate-with-usecontext/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter03/03_using-usestate-with-usecontext/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter03/03_using-usestate-with-usecontext/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter03/04_how-context-propagation-works/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "web-vitals": "^1.0.1"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/chapter03/04_how-context-propagation-works/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter03/04_how-context-propagation-works/src/App.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | memo,
3 | createContext,
4 | useContext,
5 | useRef,
6 | useEffect,
7 | useState,
8 | } from "react";
9 |
10 | const ColorContext = createContext("black");
11 |
12 | const ColorComponent = () => {
13 | const color = useContext(ColorContext);
14 | const renderCount = useRef(1);
15 | useEffect(() => {
16 | renderCount.current += 1;
17 | });
18 | return (
19 |
20 | Hello {color} (renders: {renderCount.current})
21 |
22 | );
23 | };
24 |
25 | const MemoedColorComponent = memo(ColorComponent);
26 |
27 | const DummyComponent = () => {
28 | const renderCount = useRef(1);
29 | useEffect(() => {
30 | renderCount.current += 1;
31 | });
32 | return Dummy (renders: {renderCount.current})
;
33 | };
34 |
35 | const MemoedDummyComponent = memo(DummyComponent);
36 |
37 | const Parent = () => (
38 |
39 | -
40 |
41 |
42 | -
43 |
44 |
45 | -
46 |
47 |
48 | -
49 |
50 |
51 |
52 | );
53 |
54 | const App = () => {
55 | const [color, setColor] = useState("red");
56 | return (
57 |
58 | setColor(e.target.value)} />
59 |
60 |
61 | );
62 | };
63 |
64 | export default App;
65 |
--------------------------------------------------------------------------------
/chapter03/04_how-context-propagation-works/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter03/04_how-context-propagation-works/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter03/04_how-context-propagation-works/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter03/05_pitfall-when-using-context-for-object/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "web-vitals": "^1.0.1"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/chapter03/05_pitfall-when-using-context-for-object/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter03/05_pitfall-when-using-context-for-object/src/App.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | memo,
3 | createContext,
4 | useContext,
5 | useRef,
6 | useEffect,
7 | useState,
8 | } from "react";
9 |
10 | const CountContext = createContext({ count1: 0, count2: 0 });
11 |
12 | const Counter1 = () => {
13 | const { count1 } = useContext(CountContext);
14 | const renderCount = useRef(1);
15 | useEffect(() => {
16 | renderCount.current += 1;
17 | });
18 | return (
19 |
20 | Count1: {count1} (renders: {renderCount.current})
21 |
22 | );
23 | };
24 |
25 | const MemoedCounter1 = memo(Counter1);
26 |
27 | const Counter2 = () => {
28 | const { count2 } = useContext(CountContext);
29 | const renderCount = useRef(1);
30 | useEffect(() => {
31 | renderCount.current += 1;
32 | });
33 | return (
34 |
35 | Count2: {count2} (renders: {renderCount.current})
36 |
37 | );
38 | };
39 |
40 | const MemoedCounter2 = memo(Counter2);
41 |
42 | const Parent = () => (
43 | <>
44 |
45 |
46 | >
47 | );
48 |
49 | const App = () => {
50 | const [count1, setCount1] = useState(0);
51 | const [count2, setCount2] = useState(0);
52 | return (
53 |
54 |
55 |
56 |
57 |
58 | );
59 | };
60 |
61 | export default App;
62 |
--------------------------------------------------------------------------------
/chapter03/05_pitfall-when-using-context-for-object/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter03/05_pitfall-when-using-context-for-object/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter03/05_pitfall-when-using-context-for-object/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter03/06_creating-small-state-pieces/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "web-vitals": "^1.0.1"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/chapter03/06_creating-small-state-pieces/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter03/06_creating-small-state-pieces/src/App.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Dispatch,
3 | SetStateAction,
4 | ReactNode,
5 | createContext,
6 | useContext,
7 | useState,
8 | } from "react";
9 |
10 | type CountContextType = [number, Dispatch>];
11 |
12 | const Count1Context = createContext([0, () => {}]);
13 | const Count2Context = createContext([0, () => {}]);
14 |
15 | const Counter1 = () => {
16 | const [count1, setCount1] = useContext(Count1Context);
17 | return (
18 |
19 | Count1: {count1}{" "}
20 |
21 |
22 | );
23 | };
24 |
25 | const Counter2 = () => {
26 | const [count2, setCount2] = useContext(Count2Context);
27 | return (
28 |
29 | Count2: {count2}{" "}
30 |
31 |
32 | );
33 | };
34 |
35 | const Parent = () => (
36 |
37 |
38 |
39 |
40 |
41 |
42 | );
43 |
44 | const Count1Provider = ({ children }: { children: ReactNode }) => {
45 | const [count1, setCount1] = useState(0);
46 | return (
47 |
48 | {children}
49 |
50 | );
51 | };
52 |
53 | const Count2Provider = ({ children }: { children: ReactNode }) => {
54 | const [count2, setCount2] = useState(0);
55 | return (
56 |
57 | {children}
58 |
59 | );
60 | };
61 |
62 | const App = () => (
63 |
64 |
65 |
66 |
67 |
68 | );
69 |
70 | export default App;
71 |
--------------------------------------------------------------------------------
/chapter03/06_creating-small-state-pieces/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter03/06_creating-small-state-pieces/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter03/06_creating-small-state-pieces/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter03/07_creating-one-state-with-userreducer-and-propagate-with-multiple-contexts/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "web-vitals": "^1.0.1"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/chapter03/07_creating-one-state-with-userreducer-and-propagate-with-multiple-contexts/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter03/07_creating-one-state-with-userreducer-and-propagate-with-multiple-contexts/src/App.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Dispatch,
3 | ReactNode,
4 | createContext,
5 | useContext,
6 | useReducer,
7 | } from "react";
8 |
9 | type Action = { type: "INC1" } | { type: "INC2" };
10 |
11 | const Count1Context = createContext(0);
12 | const Count2Context = createContext(0);
13 | const DispatchContext = createContext>(() => {});
14 |
15 | const Counter1 = () => {
16 | const count1 = useContext(Count1Context);
17 | const dispatch = useContext(DispatchContext);
18 | return (
19 |
20 | Count1: {count1}{" "}
21 |
22 |
23 | );
24 | };
25 |
26 | const Counter2 = () => {
27 | const count2 = useContext(Count2Context);
28 | const dispatch = useContext(DispatchContext);
29 | return (
30 |
31 | Count2: {count2}{" "}
32 |
33 |
34 | );
35 | };
36 |
37 | const Parent = () => (
38 |
39 |
40 |
41 |
42 |
43 |
44 | );
45 |
46 | const Provider = ({ children }: { children: ReactNode }) => {
47 | const [state, dispatch] = useReducer(
48 | (prev: { count1: number; count2: number }, action: Action) => {
49 | if (action.type === "INC1") {
50 | return { ...prev, count1: prev.count1 + 1 };
51 | }
52 | if (action.type === "INC2") {
53 | return { ...prev, count2: prev.count2 + 1 };
54 | }
55 | throw new Error("no matching action");
56 | },
57 | {
58 | count1: 0,
59 | count2: 0,
60 | }
61 | );
62 | return (
63 |
64 |
65 |
66 | {children}
67 |
68 |
69 |
70 | );
71 | };
72 |
73 | const App = () => (
74 |
75 |
76 |
77 | );
78 |
79 | export default App;
80 |
--------------------------------------------------------------------------------
/chapter03/07_creating-one-state-with-userreducer-and-propagate-with-multiple-contexts/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter03/07_creating-one-state-with-userreducer-and-propagate-with-multiple-contexts/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter03/07_creating-one-state-with-userreducer-and-propagate-with-multiple-contexts/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter03/08_creating-custom-hook-and-provider-component/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "web-vitals": "^1.0.1"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/chapter03/08_creating-custom-hook-and-provider-component/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter03/08_creating-custom-hook-and-provider-component/src/App.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Dispatch,
3 | SetStateAction,
4 | ReactNode,
5 | createContext,
6 | useContext,
7 | useState,
8 | } from "react";
9 |
10 | type CountContextType = [number, Dispatch>];
11 |
12 | const Count1Context = createContext(null);
13 | export const Count1Provider = ({ children }: { children: ReactNode }) => (
14 |
15 | {children}
16 |
17 | );
18 | export const useCount1 = () => {
19 | const value = useContext(Count1Context);
20 | if (value === null) throw new Error("Provider missing");
21 | return value;
22 | };
23 |
24 | const Count2Context = createContext(null);
25 | export const Count2Provider = ({ children }: { children: ReactNode }) => (
26 |
27 | {children}
28 |
29 | );
30 | export const useCount2 = () => {
31 | const value = useContext(Count2Context);
32 | if (value === null) throw new Error("Provider missing");
33 | return value;
34 | };
35 |
36 | const Counter1 = () => {
37 | const [count1, setCount1] = useCount1();
38 | return (
39 |
40 | Count1: {count1}{" "}
41 |
42 |
43 | );
44 | };
45 |
46 | const Counter2 = () => {
47 | const [count2, setCount2] = useCount2();
48 | return (
49 |
50 | Count2: {count2}{" "}
51 |
52 |
53 | );
54 | };
55 |
56 | const Parent = () => (
57 |
58 |
59 |
60 |
61 |
62 |
63 | );
64 |
65 | const App = () => (
66 |
67 |
68 |
69 |
70 |
71 | );
72 |
73 | export default App;
74 |
--------------------------------------------------------------------------------
/chapter03/08_creating-custom-hook-and-provider-component/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter03/08_creating-custom-hook-and-provider-component/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter03/08_creating-custom-hook-and-provider-component/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter03/09_factory-pattern-with-custom-hook/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "web-vitals": "^1.0.1"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/chapter03/09_factory-pattern-with-custom-hook/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter03/09_factory-pattern-with-custom-hook/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode, createContext, useContext, useState } from "react";
2 |
3 | const createStateContext = (
4 | useValue: (init?: Value) => State
5 | ) => {
6 | const StateContext = createContext(null);
7 | const StateProvider = ({
8 | initialValue,
9 | children,
10 | }: {
11 | initialValue?: Value;
12 | children?: ReactNode;
13 | }) => (
14 |
15 | {children}
16 |
17 | );
18 | const useContextState = () => {
19 | const value = useContext(StateContext);
20 | if (value === null) throw new Error("Provider missing");
21 | return value;
22 | };
23 | return [StateProvider, useContextState] as const;
24 | };
25 |
26 | const useNumberState = (init?: number) => useState(init || 0);
27 |
28 | const [Count1Provider, useCount1] = createStateContext(useNumberState);
29 | const [Count2Provider, useCount2] = createStateContext(useNumberState);
30 |
31 | const Counter1 = () => {
32 | const [count1, setCount1] = useCount1();
33 | return (
34 |
35 | Count1: {count1}{" "}
36 |
37 |
38 | );
39 | };
40 |
41 | const Counter2 = () => {
42 | const [count2, setCount2] = useCount2();
43 | return (
44 |
45 | Count2: {count2}{" "}
46 |
47 |
48 | );
49 | };
50 |
51 | const Parent = () => (
52 |
53 |
54 |
55 |
56 |
57 |
58 | );
59 |
60 | const App = () => (
61 |
62 |
63 |
64 |
65 |
66 | );
67 |
68 | export default App;
69 |
--------------------------------------------------------------------------------
/chapter03/09_factory-pattern-with-custom-hook/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter03/09_factory-pattern-with-custom-hook/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter03/09_factory-pattern-with-custom-hook/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter03/10_avoiding-provider-nesting-reduceright/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "web-vitals": "^1.0.1"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/chapter03/10_avoiding-provider-nesting-reduceright/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter03/10_avoiding-provider-nesting-reduceright/src/App.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | ReactNode,
3 | createContext,
4 | createElement,
5 | useContext,
6 | useState,
7 | } from "react";
8 |
9 | const createStateContext = (
10 | useValue: (init?: Value) => State
11 | ) => {
12 | const StateContext = createContext(null);
13 | const StateProvider = ({
14 | initialValue,
15 | children,
16 | }: {
17 | initialValue?: Value;
18 | children?: ReactNode;
19 | }) => (
20 |
21 | {children}
22 |
23 | );
24 | const useContextState = () => {
25 | const value = useContext(StateContext);
26 | if (value === null) throw new Error("Provider missing");
27 | return value;
28 | };
29 | return [StateProvider, useContextState] as const;
30 | };
31 |
32 | const useNumberState = (init?: number) => useState(init || 0);
33 |
34 | const [Count1Provider, useCount1] = createStateContext(useNumberState);
35 | const [Count2Provider, useCount2] = createStateContext(useNumberState);
36 | const [Count3Provider, useCount3] = createStateContext(useNumberState);
37 | const [Count4Provider, useCount4] = createStateContext(useNumberState);
38 | const [Count5Provider, useCount5] = createStateContext(useNumberState);
39 |
40 | const Counter1 = () => {
41 | const [count1, setCount1] = useCount1();
42 | return (
43 |
44 | Count1: {count1}{" "}
45 |
46 |
47 | );
48 | };
49 |
50 | const Counter2 = () => {
51 | const [count2, setCount2] = useCount2();
52 | return (
53 |
54 | Count2: {count2}{" "}
55 |
56 |
57 | );
58 | };
59 |
60 | const Parent = () => (
61 |
62 |
63 |
64 |
65 |
66 |
67 | );
68 |
69 | const App = () => {
70 | const providers = [
71 | [Count1Provider, { initialValue: 10 }],
72 | [Count2Provider, { initialValue: 20 }],
73 | [Count3Provider, { initialValue: 30 }],
74 | [Count4Provider, { initialValue: 40 }],
75 | [Count5Provider, { initialValue: 50 }],
76 | ] as const;
77 | return providers.reduceRight(
78 | (children, [Component, props]) => createElement(Component, props, children),
79 |
80 | );
81 | };
82 |
83 | /*
84 | const App = () => (
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | );
97 | */
98 |
99 | export default App;
100 |
--------------------------------------------------------------------------------
/chapter03/10_avoiding-provider-nesting-reduceright/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter03/10_avoiding-provider-nesting-reduceright/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter03/10_avoiding-provider-nesting-reduceright/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter04/01_naive_solution_to_module_state/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "web-vitals": "^1.0.1"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/chapter04/01_naive_solution_to_module_state/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter04/01_naive_solution_to_module_state/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 |
3 | let count = 0;
4 | const setStateFunctions = new Set<(count: number) =>void>();
5 |
6 | const Component1 = () => {
7 | const [state, setState] = useState(count);
8 | useEffect(() => {
9 | setStateFunctions.add(setState);
10 | return () => {
11 | setStateFunctions.delete(setState);
12 | }
13 | }, []);
14 | const inc = () => {
15 | count += 1;
16 | setStateFunctions.forEach((fn) => {
17 | fn(count);
18 | });
19 | };
20 | return (
21 |
22 | {state}
23 |
24 | );
25 | };
26 |
27 | const Component2 = () => {
28 | const [state, setState] = useState(count);
29 | useEffect(() => {
30 | setStateFunctions.add(setState);
31 | return () => {
32 | setStateFunctions.delete(setState);
33 | }
34 | }, []);
35 | const inc2 = () => {
36 | count += 2;
37 | setStateFunctions.forEach((fn) => {
38 | fn(count);
39 | });
40 | };
41 | return (
42 |
43 | {state}
44 |
45 | );
46 | };
47 |
48 | const App = () => (
49 | <>
50 |
51 |
52 | >
53 | );
54 |
55 | export default App;
56 |
--------------------------------------------------------------------------------
/chapter04/01_naive_solution_to_module_state/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter04/01_naive_solution_to_module_state/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter04/01_naive_solution_to_module_state/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter04/02_use_store_with_subscription/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "web-vitals": "^1.0.1"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/chapter04/02_use_store_with_subscription/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter04/02_use_store_with_subscription/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 |
3 | type Store = {
4 | getState: () => T;
5 | setState: (action: T | ((prev: T) => T)) => void;
6 | subscribe: (callback: () => void) => () => void;
7 | };
8 |
9 | const createStore = (initialState: T): Store => {
10 | let state = initialState;
11 | const callbacks = new Set<() => void>();
12 | const getState = () => state;
13 | const setState = (nextState: T | ((prev: T) => T)) => {
14 | state =
15 | typeof nextState === "function"
16 | ? (nextState as (prev: T) => T)(state)
17 | : nextState;
18 | callbacks.forEach((callback) => callback());
19 | };
20 | const subscribe = (callback: () => void) => {
21 | callbacks.add(callback);
22 | return () => {
23 | callbacks.delete(callback);
24 | };
25 | };
26 | return { getState, setState, subscribe };
27 | };
28 |
29 | const store = createStore({ count: 0 });
30 |
31 | const useStore = (store: Store) => {
32 | const [state, setState] = useState(store.getState());
33 | useEffect(() => {
34 | const unsubscribe = store.subscribe(() => {
35 | setState(store.getState());
36 | });
37 | setState(store.getState());
38 | return unsubscribe;
39 | }, [store]);
40 | return [state, store.setState] as const;
41 | };
42 |
43 | const Component1 = () => {
44 | const [state, setState] = useStore(store);
45 | const inc = () => {
46 | setState((prev) => ({
47 | ...prev,
48 | count: prev.count + 1,
49 | }));
50 | };
51 | return (
52 |
53 | {state.count}
54 |
55 | );
56 | };
57 |
58 | const Component2 = () => {
59 | const [state, setState] = useStore(store);
60 | const inc2 = () => {
61 | setState((prev) => ({
62 | ...prev,
63 | count: prev.count + 2,
64 | }));
65 | };
66 | return (
67 |
68 | {state.count}
69 |
70 | );
71 | };
72 |
73 | const App = () => (
74 | <>
75 |
76 |
77 | >
78 | );
79 |
80 | export default App;
81 |
--------------------------------------------------------------------------------
/chapter04/02_use_store_with_subscription/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter04/02_use_store_with_subscription/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter04/02_use_store_with_subscription/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter04/03_use_store_with_selector/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "web-vitals": "^1.0.1"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/chapter04/03_use_store_with_selector/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter04/03_use_store_with_selector/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect, useState } from "react";
2 |
3 | type Store = {
4 | getState: () => T;
5 | setState: (action: T | ((prev: T) => T)) => void;
6 | subscribe: (callback: () => void) => () => void;
7 | };
8 |
9 | const createStore = (initialState: T): Store => {
10 | let state = initialState;
11 | const callbacks = new Set<() => void>();
12 | const getState = () => state;
13 | const setState = (nextState: T | ((prev: T) => T)) => {
14 | state =
15 | typeof nextState === "function"
16 | ? (nextState as (prev: T) => T)(state)
17 | : nextState;
18 | callbacks.forEach((callback) => callback());
19 | };
20 | const subscribe = (callback: () => void) => {
21 | callbacks.add(callback);
22 | return () => {
23 | callbacks.delete(callback);
24 | };
25 | };
26 | return { getState, setState, subscribe };
27 | };
28 |
29 | const store = createStore({ count1: 0, count2: 0 });
30 |
31 | const useStoreSelector = (store: Store, selector: (state: T) => S) => {
32 | const [state, setState] = useState(() => selector(store.getState()));
33 | useEffect(() => {
34 | const unsubscribe = store.subscribe(() => {
35 | setState(selector(store.getState()));
36 | });
37 | setState(selector(store.getState()));
38 | return unsubscribe;
39 | }, [store, selector]);
40 | return state;
41 | };
42 |
43 | const Component1 = () => {
44 | const state = useStoreSelector(
45 | store,
46 | useCallback((state) => state.count1, [])
47 | );
48 | const inc = () => {
49 | store.setState((prev) => ({
50 | ...prev,
51 | count1: prev.count1 + 1,
52 | }));
53 | };
54 | return (
55 |
56 | count1: {state}
57 |
58 | );
59 | };
60 |
61 | const selectCount2 = (state: ReturnType) => state.count2;
62 |
63 | const Component2 = () => {
64 | const state = useStoreSelector(store, selectCount2);
65 | const inc = () => {
66 | store.setState((prev) => ({
67 | ...prev,
68 | count2: prev.count2 + 1,
69 | }));
70 | };
71 | return (
72 |
73 | count2: {state}
74 |
75 | );
76 | };
77 |
78 | const App = () => (
79 | <>
80 |
81 |
82 |
83 |
84 | >
85 | );
86 |
87 | export default App;
88 |
--------------------------------------------------------------------------------
/chapter04/03_use_store_with_selector/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter04/03_use_store_with_selector/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter04/03_use_store_with_selector/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter04/04_use_subscription_with_store/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "@types/use-subscription": "^1.0.0",
14 | "react": "^17.0.2",
15 | "react-dom": "^17.0.2",
16 | "react-scripts": "4.0.3",
17 | "typescript": "^4.1.2",
18 | "use-subscription": "^1.5.1",
19 | "web-vitals": "^1.0.1"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject"
26 | },
27 | "eslintConfig": {
28 | "extends": [
29 | "react-app",
30 | "react-app/jest"
31 | ]
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.2%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/chapter04/04_use_subscription_with_store/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter04/04_use_subscription_with_store/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { useCallback, useMemo } from "react";
2 | import { useSubscription } from "use-subscription";
3 |
4 | type Store = {
5 | getState: () => T;
6 | setState: (action: T | ((prev: T) => T)) => void;
7 | subscribe: (callback: () => void) => () => void;
8 | };
9 |
10 | const createStore = (initialState: T): Store => {
11 | let state = initialState;
12 | const callbacks = new Set<() => void>();
13 | const getState = () => state;
14 | const setState = (nextState: T | ((prev: T) => T)) => {
15 | state =
16 | typeof nextState === "function"
17 | ? (nextState as (prev: T) => T)(state)
18 | : nextState;
19 | callbacks.forEach((callback) => callback());
20 | };
21 | const subscribe = (callback: () => void) => {
22 | callbacks.add(callback);
23 | return () => {
24 | callbacks.delete(callback);
25 | };
26 | };
27 | return { getState, setState, subscribe };
28 | };
29 |
30 | const store = createStore({ count1: 0, count2: 0 });
31 |
32 | const useStoreSelector = (store: Store, selector: (state: T) => S) =>
33 | useSubscription(
34 | useMemo(
35 | () => ({
36 | getCurrentValue: () => selector(store.getState()),
37 | subscribe: store.subscribe,
38 | }),
39 | [store, selector]
40 | )
41 | );
42 |
43 | const Component1 = () => {
44 | const state = useStoreSelector(
45 | store,
46 | useCallback((state) => state.count1, [])
47 | );
48 | /*
49 | const state = useSubscription(useMemo(() => ({
50 | getCurrentValue: () => store.getState().count1,
51 | subscribe: store.subscribe,
52 | }), []));
53 | */
54 | const inc = () => {
55 | store.setState((prev) => ({
56 | ...prev,
57 | count1: prev.count1 + 1,
58 | }));
59 | };
60 | return (
61 |
62 | count1: {state}
63 |
64 | );
65 | };
66 |
67 | const selectCount2 = (state: ReturnType) => state.count2;
68 |
69 | const Component2 = () => {
70 | const state = useStoreSelector(store, selectCount2);
71 | const inc = () => {
72 | store.setState((prev) => ({
73 | ...prev,
74 | count2: prev.count2 + 1,
75 | }));
76 | };
77 | return (
78 |
79 | count2: {state}
80 |
81 | );
82 | };
83 |
84 | const App = () => (
85 | <>
86 |
87 |
88 |
89 |
90 | >
91 | );
92 |
93 | export default App;
94 |
--------------------------------------------------------------------------------
/chapter04/04_use_subscription_with_store/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter04/04_use_subscription_with_store/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter04/04_use_subscription_with_store/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter05/01_combine_context_and_subscription/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "@types/use-subscription": "^1.0.0",
14 | "react": "^17.0.2",
15 | "react-dom": "^17.0.2",
16 | "react-scripts": "4.0.3",
17 | "typescript": "^4.1.2",
18 | "use-subscription": "^1.5.1",
19 | "web-vitals": "^1.0.1"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject"
26 | },
27 | "eslintConfig": {
28 | "extends": [
29 | "react-app",
30 | "react-app/jest"
31 | ]
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.2%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/chapter05/01_combine_context_and_subscription/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter05/01_combine_context_and_subscription/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode, createContext, useContext, useRef, useMemo } from "react";
2 | import { useSubscription } from "use-subscription";
3 |
4 | type Store = {
5 | getState: () => T;
6 | setState: (action: T | ((prev: T) => T)) => void;
7 | subscribe: (callback: () => void) => () => void;
8 | };
9 |
10 | const createStore = (initialState: T): Store => {
11 | let state = initialState;
12 | const callbacks = new Set<() => void>();
13 | const getState = () => state;
14 | const setState = (nextState: T | ((prev: T) => T)) => {
15 | state =
16 | typeof nextState === "function"
17 | ? (nextState as (prev: T) => T)(state)
18 | : nextState;
19 | callbacks.forEach((callback) => callback());
20 | };
21 | const subscribe = (callback: () => void) => {
22 | callbacks.add(callback);
23 | return () => {
24 | callbacks.delete(callback);
25 | };
26 | };
27 | return { getState, setState, subscribe };
28 | };
29 |
30 | type State = { count: number; text?: string };
31 |
32 | const StoreContext = createContext>(
33 | createStore({ count: 0, text: "hello" })
34 | );
35 |
36 | const StoreProvider = ({
37 | initialState,
38 | children,
39 | }: {
40 | initialState: State;
41 | children: ReactNode;
42 | }) => {
43 | const storeRef = useRef>();
44 | if (!storeRef.current) {
45 | storeRef.current = createStore(initialState);
46 | }
47 | return (
48 |
49 | {children}
50 |
51 | );
52 | };
53 |
54 | const useSelector = (selector: (state: State) => S) => {
55 | const store = useContext(StoreContext);
56 | return useSubscription(
57 | useMemo(
58 | () => ({
59 | getCurrentValue: () => selector(store.getState()),
60 | subscribe: store.subscribe,
61 | }),
62 | [store, selector]
63 | )
64 | );
65 | };
66 |
67 | const useSetState = () => {
68 | const store = useContext(StoreContext);
69 | return store.setState;
70 | };
71 |
72 | const selectCount = (state: State) => state.count;
73 |
74 | const Component = () => {
75 | const count = useSelector(selectCount);
76 | const setState = useSetState();
77 | const inc = () => {
78 | setState((prev) => ({
79 | ...prev,
80 | count: prev.count + 1,
81 | }));
82 | };
83 | return (
84 |
85 | count: {count}
86 |
87 | );
88 | };
89 |
90 | const App = () => (
91 | <>
92 | Using default store
93 |
94 |
95 |
96 | Using store provider
97 |
98 |
99 |
100 | Using inner store provider
101 |
102 |
103 |
104 |
105 | >
106 | );
107 |
108 | export default App;
109 |
--------------------------------------------------------------------------------
/chapter05/01_combine_context_and_subscription/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter05/01_combine_context_and_subscription/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter05/01_combine_context_and_subscription/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter07/01_counter_example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "web-vitals": "^1.0.1",
18 | "zustand": "^3.5.10"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/chapter07/01_counter_example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter07/01_counter_example/src/App.tsx:
--------------------------------------------------------------------------------
1 | import create from "zustand";
2 |
3 | type StoreState = {
4 | count1: number;
5 | count2: number;
6 | inc1: () => void;
7 | inc2: () => void;
8 | };
9 |
10 | const useStore = create((set) => ({
11 | count1: 0,
12 | count2: 0,
13 | inc1: () => set((prev) => ({ count1: prev.count1 + 1 })),
14 | inc2: () => set((prev) => ({ count2: prev.count2 + 1 })),
15 | }));
16 |
17 | const selectCount1 = (state: StoreState) => state.count1;
18 | const selectInc1 = (state: StoreState) => state.inc1;
19 |
20 | const Counter1 = () => {
21 | const count1 = useStore(selectCount1);
22 | const inc1 = useStore(selectInc1);
23 | return (
24 |
25 | count1: {count1}
26 |
27 | );
28 | };
29 |
30 | const selectCount2 = (state: StoreState) => state.count2;
31 | const selectInc2 = (state: StoreState) => state.inc2;
32 |
33 | const Counter2 = () => {
34 | const count2 = useStore(selectCount2);
35 | const inc2 = useStore(selectInc2);
36 | return (
37 |
38 | count2: {count2}
39 |
40 | );
41 | };
42 |
43 | const selectTotal = (state: StoreState) => state.count1 + state.count2;
44 |
45 | const Total = () => {
46 | const total = useStore(selectTotal);
47 | return (
48 |
49 | total: {total}
50 |
51 | );
52 | };
53 |
54 | const App = () => (
55 | <>
56 |
57 |
58 |
59 | >
60 | );
61 |
62 | export default App;
63 |
--------------------------------------------------------------------------------
/chapter07/01_counter_example/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter07/01_counter_example/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter07/01_counter_example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter07/02_todo_example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "web-vitals": "^1.0.1",
18 | "zustand": "^3.5.10"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/chapter07/02_todo_example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter07/02_todo_example/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { memo, useState } from "react";
2 | import create from "zustand";
3 |
4 | type Todo = {
5 | id: number;
6 | title: string;
7 | done: boolean;
8 | };
9 |
10 | type StoreState = {
11 | todos: Todo[];
12 | addTodo: (title: string) => void;
13 | removeTodo: (id: number) => void;
14 | toggleTodo: (id: number) => void;
15 | };
16 |
17 | let nextId = 0;
18 |
19 | const useStore = create((set) => ({
20 | todos: [],
21 | addTodo: (title) =>
22 | set((prev) => ({
23 | todos: [...prev.todos, { id: ++nextId, title, done: false }],
24 | })),
25 | removeTodo: (id) =>
26 | set((prev) => ({
27 | todos: prev.todos.filter((todo) => todo.id !== id),
28 | })),
29 | toggleTodo: (id) =>
30 | set((prev) => ({
31 | todos: prev.todos.map((todo) =>
32 | todo.id === id ? { ...todo, done: !todo.done } : todo
33 | ),
34 | })),
35 | }));
36 |
37 | const selectRemoveTodo = (state: StoreState) => state.removeTodo;
38 | const selectToggleTodo = (state: StoreState) => state.toggleTodo;
39 |
40 | const TodoItem = ({ todo }: { todo: Todo }) => {
41 | const removeTodo = useStore(selectRemoveTodo);
42 | const toggleTodo = useStore(selectToggleTodo);
43 | return (
44 |
45 | toggleTodo(todo.id)}
49 | />
50 |
55 | {todo.title}
56 |
57 |
58 |
59 | );
60 | };
61 |
62 | const MemoedTodoItem = memo(TodoItem);
63 |
64 | const selectTodos = (state: StoreState) => state.todos;
65 |
66 | const TodoList = () => {
67 | const todos = useStore(selectTodos);
68 | return (
69 |
70 | {todos.map((todo) => (
71 |
72 | ))}
73 |
74 | );
75 | };
76 |
77 | const selectAddTodo = (state: StoreState) => state.addTodo;
78 |
79 | const NewTodo = () => {
80 | const addTodo = useStore(selectAddTodo);
81 | const [text, setText] = useState("");
82 | const onClick = () => {
83 | addTodo(text);
84 | setText("");
85 | };
86 | return (
87 |
88 | setText(e.target.value)} />
89 |
92 |
93 | );
94 | };
95 |
96 | const App = () => (
97 | <>
98 |
99 |
100 | >
101 | );
102 |
103 | export default App;
104 |
--------------------------------------------------------------------------------
/chapter07/02_todo_example/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter07/02_todo_example/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter07/02_todo_example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter08/01_comparison/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "jotai": "^1.3.5",
14 | "react": "^17.0.2",
15 | "react-dom": "^17.0.2",
16 | "react-scripts": "4.0.3",
17 | "typescript": "^4.1.2",
18 | "web-vitals": "^1.0.1"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/chapter08/01_comparison/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter08/01_comparison/src/AppWithContext.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Dispatch,
3 | ReactNode,
4 | SetStateAction,
5 | createContext,
6 | useContext,
7 | useState,
8 | } from "react";
9 |
10 | const CountContext = createContext(
11 | (undefined as unknown) as [number, Dispatch>]
12 | );
13 |
14 | const CountProvider = ({ children }: { children: ReactNode }) => (
15 | {children}
16 | );
17 |
18 | const Counter1 = () => {
19 | const [count, setCount] = useContext(CountContext);
20 | const inc = () => setCount((c) => c + 1);
21 | return (
22 | <>
23 | {count}
24 | >
25 | );
26 | };
27 |
28 | const Counter2 = () => {
29 | const [count, setCount] = useContext(CountContext);
30 | const inc = () => setCount((c) => c + 1);
31 | return (
32 | <>
33 | {count}
34 | >
35 | );
36 | };
37 |
38 | const App = () => (
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | );
48 |
49 | export default App;
50 |
--------------------------------------------------------------------------------
/chapter08/01_comparison/src/AppWithJotai.tsx:
--------------------------------------------------------------------------------
1 | import { atom, useAtom } from "jotai";
2 |
3 | const countAtom = atom(0);
4 |
5 | const Counter1 = () => {
6 | const [count, setCount] = useAtom(countAtom);
7 | const inc = () => setCount((c) => c + 1);
8 | return (
9 | <>
10 | {count}
11 | >
12 | );
13 | };
14 |
15 | const Counter2 = () => {
16 | const [count, setCount] = useAtom(countAtom);
17 | const inc = () => setCount((c) => c + 1);
18 | return (
19 | <>
20 | {count}
21 | >
22 | );
23 | };
24 |
25 | const App = () => (
26 | <>
27 |
28 |
29 |
30 |
31 |
32 |
33 | >
34 | );
35 |
36 | export default App;
37 |
--------------------------------------------------------------------------------
/chapter08/01_comparison/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import AppWithContext from './AppWithContext';
4 | import AppWithJotai from './AppWithJotai';
5 |
6 | ReactDOM.render(
7 |
8 | App with Context
9 |
10 | App with Jotai
11 |
12 | ,
13 | document.getElementById('root')
14 | );
15 |
--------------------------------------------------------------------------------
/chapter08/01_comparison/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter08/01_comparison/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter08/02_counter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "jotai": "^1.3.5",
14 | "react": "^17.0.2",
15 | "react-dom": "^17.0.2",
16 | "react-scripts": "4.0.3",
17 | "typescript": "^4.1.2",
18 | "web-vitals": "^1.0.1"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/chapter08/02_counter/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter08/02_counter/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { atom, useAtom } from "jotai";
2 |
3 | const count1Atom = atom(0);
4 | const count2Atom = atom(0);
5 |
6 | const Counter = ({ countAtom }: { countAtom: typeof count1Atom }) => {
7 | const [count, setCount] = useAtom(countAtom);
8 | const inc = () => setCount((c) => c + 1);
9 | return (
10 | <>
11 | {count}
12 | >
13 | );
14 | };
15 |
16 | const totalAtom = atom((get) => get(count1Atom) + get(count2Atom));
17 |
18 | const Total = () => {
19 | const [total] = useAtom(totalAtom);
20 | return <>{total}>;
21 | };
22 |
23 | const App = () => (
24 | <>
25 | () + ()
26 | =
27 | >
28 | );
29 |
30 | export default App;
31 |
--------------------------------------------------------------------------------
/chapter08/02_counter/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter08/02_counter/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter08/02_counter/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter08/03_provider/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "jotai": "^1.3.5",
14 | "react": "^17.0.2",
15 | "react-dom": "^17.0.2",
16 | "react-scripts": "4.0.3",
17 | "typescript": "^4.1.2",
18 | "web-vitals": "^1.0.1"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/chapter08/03_provider/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter08/03_provider/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { atom, useAtom, Provider } from "jotai";
2 |
3 | const countAtom = atom(0);
4 |
5 | const Counter = () => {
6 | const [count, setCount] = useAtom(countAtom);
7 | const inc = () => setCount((c) => c + 1);
8 | return (
9 | <>
10 | {count}
11 | >
12 | );
13 | };
14 |
15 | const App = () => (
16 | <>
17 |
18 | First Provider
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | Second Provider
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | >
36 | );
37 |
38 | export default App;
39 |
--------------------------------------------------------------------------------
/chapter08/03_provider/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter08/03_provider/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter08/03_provider/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter08/04_todo_app_single_atom/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "jotai": "^1.3.5",
14 | "nanoid": "^3.1.25",
15 | "react": "^17.0.2",
16 | "react-dom": "^17.0.2",
17 | "react-scripts": "4.0.3",
18 | "typescript": "^4.1.2",
19 | "web-vitals": "^1.0.1"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject"
26 | },
27 | "eslintConfig": {
28 | "extends": [
29 | "react-app",
30 | "react-app/jest"
31 | ]
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.2%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/chapter08/04_todo_app_single_atom/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter08/04_todo_app_single_atom/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { memo, useCallback, useState } from "react";
2 | import { atom, useAtom } from "jotai";
3 | import { nanoid } from "nanoid";
4 |
5 | type Todo = {
6 | id: string;
7 | title: string;
8 | done: boolean;
9 | };
10 |
11 | const todosAtom = atom([]);
12 |
13 | const TodoItem = ({
14 | todo,
15 | removeTodo,
16 | toggleTodo,
17 | }: {
18 | todo: Todo;
19 | removeTodo: (id: string) => void;
20 | toggleTodo: (id: string) => void;
21 | }) => {
22 | return (
23 |
24 | toggleTodo(todo.id)}
28 | />
29 |
34 | {todo.title}
35 |
36 |
37 |
38 | );
39 | };
40 |
41 | const MemoedTodoItem = memo(TodoItem);
42 |
43 | const TodoList = () => {
44 | const [todos, setTodos] = useAtom(todosAtom);
45 | const removeTodo = useCallback(
46 | (id: string) => setTodos((prev) => prev.filter((item) => item.id !== id)),
47 | [setTodos]
48 | );
49 | const toggleTodo = useCallback(
50 | (id: string) =>
51 | setTodos((prev) =>
52 | prev.map((item) =>
53 | item.id === id ? { ...item, done: !item.done } : item
54 | )
55 | ),
56 | [setTodos]
57 | );
58 | return (
59 |
60 | {todos.map((todo) => (
61 |
67 | ))}
68 |
69 | );
70 | };
71 |
72 | const NewTodo = () => {
73 | const [, setTodos] = useAtom(todosAtom);
74 | const [text, setText] = useState("");
75 | const onClick = () => {
76 | setTodos((prev) => [...prev, { id: nanoid(), title: text, done: false }]);
77 | setText("");
78 | };
79 | return (
80 |
81 | setText(e.target.value)} />
82 |
85 |
86 | );
87 | };
88 |
89 | const App = () => (
90 | <>
91 |
92 |
93 | >
94 | );
95 |
96 | export default App;
97 |
--------------------------------------------------------------------------------
/chapter08/04_todo_app_single_atom/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter08/04_todo_app_single_atom/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter08/04_todo_app_single_atom/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter08/05_todo_app_atoms_in_atom/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "jotai": "^1.3.5",
14 | "react": "^17.0.2",
15 | "react-dom": "^17.0.2",
16 | "react-scripts": "4.0.3",
17 | "typescript": "^4.1.2",
18 | "web-vitals": "^1.0.1"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/chapter08/05_todo_app_atoms_in_atom/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter08/05_todo_app_atoms_in_atom/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { memo, useCallback, useState } from "react";
2 | import { PrimitiveAtom, atom, useAtom } from "jotai";
3 |
4 | type Todo = {
5 | title: string;
6 | done: boolean;
7 | };
8 |
9 | type TodoAtom = PrimitiveAtom;
10 |
11 | const todoAtomsAtom = atom([]);
12 |
13 | const TodoItem = ({
14 | todoAtom,
15 | remove,
16 | }: {
17 | todoAtom: TodoAtom;
18 | remove: (todoAtom: TodoAtom) => void;
19 | }) => {
20 | const [todo, setTodo] = useAtom(todoAtom);
21 | return (
22 |
23 | setTodo((prev) => ({ ...prev, done: !prev.done }))}
27 | />
28 |
33 | {todo.title}
34 |
35 |
36 |
37 | );
38 | };
39 |
40 | const MemoedTodoItem = memo(TodoItem);
41 |
42 | const TodoList = () => {
43 | const [todoAtoms, setTodoAtoms] = useAtom(todoAtomsAtom);
44 | const remove = useCallback(
45 | (todoAtom: TodoAtom) =>
46 | setTodoAtoms((prev) => prev.filter((item) => item !== todoAtom)),
47 | [setTodoAtoms]
48 | );
49 | return (
50 |
51 | {todoAtoms.map((todoAtom) => (
52 |
57 | ))}
58 |
59 | );
60 | };
61 |
62 | const NewTodo = () => {
63 | const [, setTodoAtoms] = useAtom(todoAtomsAtom);
64 | const [text, setText] = useState("");
65 | const onClick = () => {
66 | setTodoAtoms((prev) => [...prev, atom({ title: text, done: false })]);
67 | setText("");
68 | };
69 | return (
70 |
71 | setText(e.target.value)} />
72 |
75 |
76 | );
77 | };
78 |
79 | const App = () => (
80 | <>
81 |
82 |
83 | >
84 | );
85 |
86 | export default App;
87 |
--------------------------------------------------------------------------------
/chapter08/05_todo_app_atoms_in_atom/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter08/05_todo_app_atoms_in_atom/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter08/05_todo_app_atoms_in_atom/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter09/01_counter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "valtio": "^1.2.4",
18 | "web-vitals": "^1.0.1"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/chapter09/01_counter/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter09/01_counter/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { proxy, useSnapshot } from "valtio";
2 |
3 | const state = proxy({
4 | count1: 0,
5 | count2: 0,
6 | });
7 |
8 | const Counter1 = () => {
9 | const snap = useSnapshot(state);
10 | const inc = () => ++state.count1;
11 | return (
12 | <>
13 | {snap.count1}
14 | >
15 | );
16 | };
17 |
18 | const Counter2 = () => {
19 | const snap = useSnapshot(state);
20 | const inc = () => ++state.count2;
21 | return (
22 | <>
23 | {snap.count2}
24 | >
25 | );
26 | };
27 |
28 | const App = () => (
29 | <>
30 |
31 |
32 |
33 |
34 |
35 |
36 | >
37 | );
38 |
39 | export default App;
40 |
--------------------------------------------------------------------------------
/chapter09/01_counter/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter09/01_counter/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter09/01_counter/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter09/02_todo_app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "nanoid": "^3.1.25",
14 | "react": "^17.0.2",
15 | "react-dom": "^17.0.2",
16 | "react-scripts": "4.0.3",
17 | "typescript": "^4.1.2",
18 | "valtio": "^1.2.4",
19 | "web-vitals": "^1.0.1"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject"
26 | },
27 | "eslintConfig": {
28 | "extends": [
29 | "react-app",
30 | "react-app/jest"
31 | ]
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.2%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/chapter09/02_todo_app/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter09/02_todo_app/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { memo, useState } from "react";
2 | import { proxy, useSnapshot } from "valtio";
3 | import { nanoid } from "nanoid";
4 |
5 | type Todo = {
6 | id: string;
7 | title: string;
8 | done: boolean;
9 | };
10 |
11 | const state = proxy<{ todos: Todo[] }>({
12 | todos: [],
13 | });
14 |
15 | const createTodo = (title: string) => {
16 | state.todos.push({
17 | id: nanoid(),
18 | title,
19 | done: false,
20 | });
21 | };
22 |
23 | const removeTodo = (id: string) => {
24 | const index = state.todos.findIndex((item) => item.id === id);
25 | state.todos.splice(index, 1);
26 | };
27 |
28 | const toggleTodo = (id: string) => {
29 | const index = state.todos.findIndex((item) => item.id === id);
30 | state.todos[index].done = !state.todos[index].done;
31 | };
32 |
33 | const TodoItem = ({
34 | id,
35 | title,
36 | done,
37 | }: {
38 | id: string;
39 | title: string;
40 | done: boolean;
41 | }) => {
42 | return (
43 |
44 | toggleTodo(id)} />
45 |
50 | {title}
51 |
52 |
53 |
54 | );
55 | };
56 |
57 | const MemoedTodoItem = memo(TodoItem);
58 |
59 | const TodoList = () => {
60 | const { todos } = useSnapshot(state);
61 | return (
62 |
63 | {todos.map((todo) => (
64 |
70 | ))}
71 |
72 | );
73 | };
74 |
75 | const NewTodo = () => {
76 | const [text, setText] = useState("");
77 | const onClick = () => {
78 | createTodo(text);
79 | setText("");
80 | };
81 | return (
82 |
83 | setText(e.target.value)} />
84 |
87 |
88 | );
89 | };
90 |
91 | const App = () => (
92 | <>
93 |
94 |
95 | >
96 | );
97 |
98 | export default App;
99 |
--------------------------------------------------------------------------------
/chapter09/02_todo_app/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter09/02_todo_app/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter09/02_todo_app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter09/03_another_todo_app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "nanoid": "^3.1.25",
14 | "react": "^17.0.2",
15 | "react-dom": "^17.0.2",
16 | "react-scripts": "4.0.3",
17 | "typescript": "^4.1.2",
18 | "valtio": "^1.2.4",
19 | "web-vitals": "^1.0.1"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject"
26 | },
27 | "eslintConfig": {
28 | "extends": [
29 | "react-app",
30 | "react-app/jest"
31 | ]
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.2%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/chapter09/03_another_todo_app/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter09/03_another_todo_app/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { memo, useState } from "react";
2 | import { proxy, useSnapshot } from "valtio";
3 | import { nanoid } from "nanoid";
4 |
5 | type Todo = {
6 | id: string;
7 | title: string;
8 | done: boolean;
9 | };
10 |
11 | const state = proxy<{ todos: Todo[] }>({
12 | todos: [],
13 | });
14 |
15 | const createTodo = (title: string) => {
16 | state.todos.push({
17 | id: nanoid(),
18 | title,
19 | done: false,
20 | });
21 | };
22 |
23 | const removeTodo = (id: string) => {
24 | const index = state.todos.findIndex((item) => item.id === id);
25 | state.todos.splice(index, 1);
26 | };
27 |
28 | const toggleTodo = (id: string) => {
29 | const index = state.todos.findIndex((item) => item.id === id);
30 | state.todos[index].done = !state.todos[index].done;
31 | };
32 |
33 | const TodoItem = ({ id }: { id: string }) => {
34 | const todoState = state.todos.find((todo) => todo.id === id);
35 | if (!todoState) {
36 | throw new Error("invalid todo id");
37 | }
38 | const { title, done } = useSnapshot(todoState);
39 | return (
40 |
41 | toggleTodo(id)} />
42 |
47 | {title}
48 |
49 |
50 |
51 | );
52 | };
53 |
54 | const MemoedTodoItem = memo(TodoItem);
55 |
56 | const TodoList = () => {
57 | const { todos } = useSnapshot(state);
58 | const todoIds = todos.map((todo) => todo.id);
59 | return (
60 |
61 | {todoIds.map((todoId) => (
62 |
63 | ))}
64 |
65 | );
66 | };
67 |
68 | const NewTodo = () => {
69 | const [text, setText] = useState("");
70 | const onClick = () => {
71 | createTodo(text);
72 | setText("");
73 | };
74 | return (
75 |
76 | setText(e.target.value)} />
77 |
80 |
81 | );
82 | };
83 |
84 | const App = () => (
85 | <>
86 |
87 |
88 | >
89 | );
90 |
91 | export default App;
92 |
--------------------------------------------------------------------------------
/chapter09/03_another_todo_app/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter09/03_another_todo_app/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter09/03_another_todo_app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter10/01_bare_context/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "web-vitals": "^1.0.1"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/chapter10/01_bare_context/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter10/01_bare_context/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode, createContext, useContext, useState } from "react";
2 |
3 | const useValue = () => useState({ count: 0, text: "hello" });
4 |
5 | const StateContext = createContext | null>(null);
6 |
7 | const Provider = ({ children }: { children: ReactNode }) => (
8 | {children}
9 | );
10 |
11 | const useStateContext = () => {
12 | const contextValue = useContext(StateContext);
13 | if (contextValue === null) {
14 | throw new Error("Please use Provider");
15 | }
16 | return contextValue;
17 | };
18 |
19 | const Counter = () => {
20 | const [state, setState] = useStateContext();
21 | const inc = () => {
22 | setState((prev) => ({ ...prev, count: prev.count + 1 }));
23 | };
24 | return (
25 |
26 | count: {state.count}
27 |
28 | );
29 | };
30 |
31 | const TextBox = () => {
32 | const [state, setState] = useStateContext();
33 | const setText = (text: string) => {
34 | setState((prev) => ({ ...prev, text }));
35 | };
36 | return (
37 |
38 | setText(e.target.value)} />
39 |
40 | );
41 | };
42 |
43 | const App = () => (
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | );
53 |
54 | export default App;
55 |
--------------------------------------------------------------------------------
/chapter10/01_bare_context/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter10/01_bare_context/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter10/01_bare_context/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter10/02_with_usestate/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "react-tracked": "^1.7.4",
17 | "typescript": "^4.1.2",
18 | "web-vitals": "^1.0.1"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/chapter10/02_with_usestate/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter10/02_with_usestate/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { createContainer } from "react-tracked";
3 |
4 | const useValue = () => useState({ count: 0, text: "hello" });
5 |
6 | const { Provider, useTracked } = createContainer(useValue);
7 |
8 | const Counter = () => {
9 | const [state, setState] = useTracked();
10 | const inc = () => {
11 | setState((prev) => ({ ...prev, count: prev.count + 1 }));
12 | };
13 | return (
14 |
15 | count: {state.count}
16 |
17 | );
18 | };
19 |
20 | const TextBox = () => {
21 | const [state, setState] = useTracked();
22 | const setText = (text: string) => {
23 | setState((prev) => ({ ...prev, text }));
24 | };
25 | return (
26 |
27 | setText(e.target.value)} />
28 |
29 | );
30 | };
31 |
32 | const App = () => (
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | );
42 |
43 | export default App;
44 |
--------------------------------------------------------------------------------
/chapter10/02_with_usestate/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter10/02_with_usestate/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter10/02_with_usestate/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter10/03_with_usereducer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "react-tracked": "^1.7.4",
17 | "typescript": "^4.1.2",
18 | "web-vitals": "^1.0.1"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/chapter10/03_with_usereducer/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter10/03_with_usereducer/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useReducer } from "react";
2 | import { createContainer } from "react-tracked";
3 |
4 | const useValue = () => {
5 | type State = { count: number; text: string };
6 | type Action = { type: "INC" } | { type: "SET_TEXT"; text: string };
7 | const [state, dispatch] = useReducer(
8 | (state: State, action: Action) => {
9 | if (action.type === "INC") {
10 | return { ...state, count: state.count + 1 };
11 | }
12 | if (action.type === "SET_TEXT") {
13 | return { ...state, text: action.text };
14 | }
15 | throw new Error("unknown action type");
16 | },
17 | { count: 0, text: "hello" }
18 | );
19 | useEffect(() => {
20 | console.log("latest state", state);
21 | }, [state]);
22 | return [state, dispatch] as const;
23 | };
24 |
25 | const { Provider, useTracked } = createContainer(useValue);
26 |
27 | const Counter = () => {
28 | const [state, dispatch] = useTracked();
29 | const inc = () => dispatch({ type: "INC" });
30 | return (
31 |
32 | count: {state.count}
33 |
34 | );
35 | };
36 |
37 | const TextBox = () => {
38 | const [state, dispatch] = useTracked();
39 | const setText = (text: string) => {
40 | dispatch({ type: "SET_TEXT", text });
41 | };
42 | return (
43 |
44 | setText(e.target.value)} />
45 |
46 | );
47 | };
48 |
49 | const App = () => (
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | );
59 |
60 | export default App;
61 |
--------------------------------------------------------------------------------
/chapter10/03_with_usereducer/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter10/03_with_usereducer/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter10/03_with_usereducer/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter10/04_with_reactredux/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-redux": "^7.2.6",
16 | "react-scripts": "4.0.3",
17 | "react-tracked": "^1.7.4",
18 | "redux": "^4.1.2",
19 | "typescript": "^4.1.2",
20 | "web-vitals": "^1.0.1"
21 | },
22 | "scripts": {
23 | "start": "react-scripts start",
24 | "build": "react-scripts build",
25 | "test": "react-scripts test",
26 | "eject": "react-scripts eject"
27 | },
28 | "eslintConfig": {
29 | "extends": [
30 | "react-app",
31 | "react-app/jest"
32 | ]
33 | },
34 | "browserslist": {
35 | "production": [
36 | ">0.2%",
37 | "not dead",
38 | "not op_mini all"
39 | ],
40 | "development": [
41 | "last 1 chrome version",
42 | "last 1 firefox version",
43 | "last 1 safari version"
44 | ]
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/chapter10/04_with_reactredux/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter10/04_with_reactredux/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { createStore } from "redux";
2 | import { Provider, useDispatch, useSelector } from "react-redux";
3 | import { createTrackedSelector } from "react-tracked";
4 |
5 | type State = { count: number; text: string };
6 | type Action = { type: "INC" } | { type: "SET_TEXT"; text: string };
7 |
8 | const initialState: State = { count: 0, text: "hello" };
9 | const reducer = (state = initialState, action: Action) => {
10 | if (action.type === "INC") {
11 | return { ...state, count: state.count + 1 };
12 | }
13 | if (action.type === "SET_TEXT") {
14 | return { ...state, text: action.text };
15 | }
16 | return state
17 | };
18 |
19 | const store = createStore(reducer);
20 | const useTrackedState = createTrackedSelector(useSelector);
21 |
22 | const Counter = () => {
23 | const dispatch = useDispatch();
24 | const { count } = useTrackedState();
25 | const inc = () => dispatch({ type: "INC" });
26 | return (
27 |
28 | count: {count}
29 |
30 | );
31 | };
32 |
33 | const TextBox = () => {
34 | const dispatch = useDispatch();
35 | const state = useTrackedState();
36 | const setText = (text: string) => {
37 | dispatch({ type: "SET_TEXT", text });
38 | };
39 | return (
40 |
41 | setText(e.target.value)} />
42 |
43 | );
44 | };
45 |
46 | const App = () => (
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | );
56 |
57 | export default App;
58 |
--------------------------------------------------------------------------------
/chapter10/04_with_reactredux/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter10/04_with_reactredux/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter10/04_with_reactredux/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter11/01_redux_counter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@reduxjs/toolkit": "^1.6.2",
7 | "@testing-library/jest-dom": "^5.11.4",
8 | "@testing-library/react": "^11.1.0",
9 | "@testing-library/user-event": "^12.1.10",
10 | "@types/jest": "^26.0.15",
11 | "@types/node": "^12.0.0",
12 | "@types/react": "^17.0.0",
13 | "@types/react-dom": "^17.0.0",
14 | "react": "^17.0.2",
15 | "react-dom": "^17.0.2",
16 | "react-redux": "^7.2.6",
17 | "react-scripts": "4.0.3",
18 | "typescript": "^4.1.2",
19 | "web-vitals": "^1.0.1"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject"
26 | },
27 | "eslintConfig": {
28 | "extends": [
29 | "react-app",
30 | "react-app/jest"
31 | ]
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.2%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/chapter11/01_redux_counter/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter11/01_redux_counter/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Provider } from "react-redux";
2 | import { store } from "./app/store";
3 | import { Counter } from "./features/counter/Counter";
4 |
5 | const App = () => (
6 |
7 |
8 |
9 |
10 |
11 |
12 | );
13 |
14 | export default App;
15 |
--------------------------------------------------------------------------------
/chapter11/01_redux_counter/src/app/store.ts:
--------------------------------------------------------------------------------
1 | import { configureStore } from "@reduxjs/toolkit";
2 | import counterReducer from "../features/counter/counterSlice";
3 |
4 | export const store = configureStore({
5 | reducer: {
6 | counter: counterReducer,
7 | },
8 | });
9 |
--------------------------------------------------------------------------------
/chapter11/01_redux_counter/src/features/counter/Counter.tsx:
--------------------------------------------------------------------------------
1 | import { useSelector, useDispatch } from "react-redux";
2 | import { decrement, increment } from "./counterSlice";
3 |
4 | export function Counter() {
5 | const count = useSelector(
6 | (state: { counter: { value: number } }) => state.counter.value
7 | );
8 | const dispatch = useDispatch();
9 | return (
10 |
11 |
12 | {count}
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/chapter11/01_redux_counter/src/features/counter/counterSlice.ts:
--------------------------------------------------------------------------------
1 | import { createSlice, PayloadAction } from "@reduxjs/toolkit";
2 |
3 | const initialState = {
4 | value: 0,
5 | };
6 |
7 | export const counterSlice = createSlice({
8 | name: "counter",
9 | initialState,
10 | reducers: {
11 | increment: (state) => {
12 | state.value += 1;
13 | },
14 | decrement: (state) => {
15 | state.value -= 1;
16 | },
17 | incrementByAmount: (state, action: PayloadAction) => {
18 | state.value += action.payload;
19 | },
20 | },
21 | });
22 |
23 | export const { increment, decrement, incrementByAmount } = counterSlice.actions;
24 | export default counterSlice.reducer;
25 |
--------------------------------------------------------------------------------
/chapter11/01_redux_counter/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter11/01_redux_counter/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter11/01_redux_counter/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter11/02_zustand_counter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "web-vitals": "^1.0.1",
18 | "zustand": "^3.6.5"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/chapter11/02_zustand_counter/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter11/02_zustand_counter/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Counter } from "./Counter";
2 |
3 | const App = () => (
4 |
5 |
6 |
7 |
8 | );
9 |
10 | export default App;
11 |
--------------------------------------------------------------------------------
/chapter11/02_zustand_counter/src/Counter.tsx:
--------------------------------------------------------------------------------
1 | import { useStore } from "./store";
2 |
3 | export function Counter() {
4 | const count = useStore((state) => state.counter.value);
5 | const { increment, decrement } = useStore((state) => state.counterActions);
6 | return (
7 |
8 |
9 |
10 | {count}
11 |
12 |
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/chapter11/02_zustand_counter/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter11/02_zustand_counter/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter11/02_zustand_counter/src/store.ts:
--------------------------------------------------------------------------------
1 | import create from "zustand";
2 |
3 | type State = {
4 | counter: {
5 | value: number;
6 | };
7 | counterActions: {
8 | increment: () => void;
9 | decrement: () => void;
10 | incrementByAmount: (amount: number) => void;
11 | };
12 | };
13 |
14 | export const useStore = create((set) => ({
15 | counter: { value: 0 },
16 | counterActions: {
17 | increment: () =>
18 | set((state) => ({ counter: { value: state.counter.value + 1 } })),
19 | decrement: () =>
20 | set((state) => ({ counter: { value: state.counter.value - 1 } })),
21 | incrementByAmount: (amount: number) =>
22 | set((state) => ({ counter: { value: state.counter.value + amount } })),
23 | },
24 | }));
25 |
--------------------------------------------------------------------------------
/chapter11/02_zustand_counter/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter11/03_recoil_charcounter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "recoil": "^0.5.2",
17 | "typescript": "^4.1.2",
18 | "web-vitals": "^1.0.1"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/chapter11/03_recoil_charcounter/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter11/03_recoil_charcounter/src/App.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | RecoilRoot,
3 | atom,
4 | selector,
5 | useRecoilState,
6 | useRecoilValue,
7 | } from "recoil";
8 |
9 | const textState = atom({
10 | key: "textState",
11 | default: "",
12 | });
13 |
14 | const TextInput = () => {
15 | const [text, setText] = useRecoilState(textState);
16 | return (
17 |
18 | {
22 | setText(event.target.value);
23 | }}
24 | />
25 |
26 | Echo: {text}
27 |
28 | );
29 | };
30 |
31 | const charCountState = selector({
32 | key: "charCountState",
33 | get: ({ get }) => get(textState).length,
34 | });
35 |
36 | const CharacterCount = () => {
37 | const count = useRecoilValue(charCountState);
38 | return <>Character Count: {count}>;
39 | };
40 |
41 | const CharacterCounter = () => (
42 |
43 |
44 |
45 |
46 | );
47 |
48 | const App = () => (
49 |
50 |
51 |
52 | );
53 |
54 | export default App;
55 |
--------------------------------------------------------------------------------
/chapter11/03_recoil_charcounter/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter11/03_recoil_charcounter/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter11/03_recoil_charcounter/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter11/04_jotai_charcounter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "jotai": "^1.4.6",
14 | "react": "^17.0.2",
15 | "react-dom": "^17.0.2",
16 | "react-scripts": "4.0.3",
17 | "typescript": "^4.1.2",
18 | "web-vitals": "^1.0.1"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/chapter11/04_jotai_charcounter/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter11/04_jotai_charcounter/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { atom, useAtom } from "jotai";
2 |
3 | const textAtom = atom("");
4 |
5 | const TextInput = () => {
6 | const [text, setText] = useAtom(textAtom);
7 | return (
8 |
9 | {
13 | setText(event.target.value);
14 | }}
15 | />
16 |
17 | Echo: {text}
18 |
19 | );
20 | };
21 |
22 | const charCountAtom = atom((get) => get(textAtom).length);
23 |
24 | const CharacterCount = () => {
25 | const [count] = useAtom(charCountAtom);
26 | return <>Character Count: {count}>;
27 | };
28 |
29 | const CharacterCounter = () => (
30 |
31 |
32 |
33 |
34 | );
35 |
36 | const App = () => (
37 | <>
38 |
39 | >
40 | );
41 |
42 | export default App;
43 |
--------------------------------------------------------------------------------
/chapter11/04_jotai_charcounter/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter11/04_jotai_charcounter/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter11/04_jotai_charcounter/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter11/05_mobx_timer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "mobx": "^6.3.8",
14 | "mobx-react": "^7.2.1",
15 | "react": "^17.0.2",
16 | "react-dom": "^17.0.2",
17 | "react-scripts": "4.0.3",
18 | "typescript": "^4.1.2",
19 | "web-vitals": "^1.0.1"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject"
26 | },
27 | "eslintConfig": {
28 | "extends": [
29 | "react-app",
30 | "react-app/jest"
31 | ]
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.2%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/chapter11/05_mobx_timer/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter11/05_mobx_timer/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { makeAutoObservable } from "mobx";
2 | import { observer } from "mobx-react";
3 |
4 | class Timer {
5 | secondsPassed = 0;
6 | constructor() {
7 | makeAutoObservable(this);
8 | }
9 | increase() {
10 | this.secondsPassed += 1;
11 | }
12 | reset() {
13 | this.secondsPassed = 0;
14 | }
15 | }
16 |
17 | const myTimer = new Timer();
18 |
19 | setInterval(() => {
20 | myTimer.increase();
21 | }, 1000);
22 |
23 | const TimerView = observer(({ timer }: { timer: Timer }) => (
24 |
27 | ));
28 |
29 | const App = () => (
30 | <>
31 |
32 | >
33 | );
34 |
35 | export default App;
36 |
--------------------------------------------------------------------------------
/chapter11/05_mobx_timer/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter11/05_mobx_timer/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter11/05_mobx_timer/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/chapter11/06_valtio_timer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "@types/jest": "^26.0.15",
10 | "@types/node": "^12.0.0",
11 | "@types/react": "^17.0.0",
12 | "@types/react-dom": "^17.0.0",
13 | "react": "^17.0.2",
14 | "react-dom": "^17.0.2",
15 | "react-scripts": "4.0.3",
16 | "typescript": "^4.1.2",
17 | "valtio": "^1.2.7",
18 | "web-vitals": "^1.0.1"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/chapter11/06_valtio_timer/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/chapter11/06_valtio_timer/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { proxy, useSnapshot } from "valtio";
2 |
3 | const myTimer = proxy({
4 | secondsPassed: 0,
5 | increase: () => {
6 | myTimer.secondsPassed += 1;
7 | },
8 | reset: () => {
9 | myTimer.secondsPassed = 0;
10 | },
11 | });
12 |
13 | setInterval(() => {
14 | myTimer.increase();
15 | }, 1000);
16 |
17 | const TimerView = ({ timer }: { timer: typeof myTimer }) => {
18 | const snap = useSnapshot(timer);
19 | return (
20 |
23 | );
24 | };
25 |
26 | const App = () => (
27 | <>
28 |
29 | >
30 | );
31 |
32 | export default App;
33 |
--------------------------------------------------------------------------------
/chapter11/06_valtio_timer/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------
/chapter11/06_valtio_timer/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/chapter11/06_valtio_timer/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/cover.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wikibook/msmrh/27b3993aa81d9c034ac72ca435205e6cb14a9ff1/cover.jpg
--------------------------------------------------------------------------------