Skip to content

Commit 5ee5270

Browse files
committed
fix: issues resolved with final example
1 parent ddf633d commit 5ee5270

File tree

11 files changed

+260
-67
lines changed

11 files changed

+260
-67
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ index.d.ts
44
coverage/
55
todo
66
build/
7+
index.min.js

.npmignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@ public/
44
example/
55
config/
66
scripts/
7-
build/
7+
build/
8+
index.js
9+
index.ts
10+
index.d.ts

config/paths.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ module.exports = {
5656
appSrc: resolveApp('example'),
5757
appTsConfig: resolveApp('tsconfig.json'),
5858
appJsConfig: resolveApp('jsconfig.json'),
59-
pluginJs: resolveApp('index'),
59+
pluginJs: resolveApp('index.min'),
6060
yarnLockFile: resolveApp('yarn.lock'),
6161
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
6262
proxySetup: resolveApp('src/setupProxy.js'),

example/App.test.tsx

Lines changed: 0 additions & 9 deletions
This file was deleted.

example/App.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,27 @@ import TodoList from './components/TodoList';
44
import { TodosContextProvider } from './contexts/TodosContext';
55

66
const App = () => {
7+
const mainStyle: React.CSSProperties = {
8+
display: 'flex',
9+
alignItems: 'center',
10+
justifyContent: 'center',
11+
flexDirection: 'column',
12+
alignSelf: 'center',
13+
height: '100vh',
14+
};
15+
16+
const imgStyle: React.CSSProperties = {
17+
maxHeight: 200,
18+
};
19+
720
return (
8-
<TodosContextProvider>
21+
<TodosContextProvider>
22+
<div id={'main'} style={mainStyle}>
23+
<img src={require('./logo.svg')} alt={'react-logo'} style={imgStyle} />
924
<TodoForm />
1025
<TodoList />
11-
</TodosContextProvider>
26+
</div>
27+
</TodosContextProvider>
1228
);
1329
};
1430

example/components/TodoForm.tsx

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ function TodoForm() {
55
const [value, setValue] = useState('');
66
const dispatch = useTodosDispatch();
77

8+
const formStyles: React.CSSProperties = {
9+
display: 'flex',
10+
justifyContent: 'center',
11+
width: '50%'
12+
};
13+
814
const onSubmit = (e: React.FormEvent) => {
915
e.preventDefault();
1016
dispatch({
@@ -15,15 +21,28 @@ function TodoForm() {
1521
};
1622

1723
return (
18-
<form onSubmit={onSubmit}>
19-
<input
20-
value={value}
21-
placeholder="What are you planning to do?"
22-
onChange={e => setValue(e.target.value)}
23-
/>
24-
<button>Create</button>
25-
</form>
26-
);
24+
<form onSubmit={onSubmit} style={formStyles}>
25+
<input
26+
value={value}
27+
placeholder='What are you planning to do?'
28+
onChange={(e) => setValue(e.target.value)}
29+
style={{
30+
padding: 10,
31+
borderRadius: 5,
32+
flexBasis: '35%'
33+
}}
34+
/>
35+
<button
36+
style={{
37+
padding: 10,
38+
marginLeft: 10,
39+
borderRadius: 5,
40+
}}
41+
>
42+
Create
43+
</button>
44+
</form>
45+
);
2746
}
2847

2948
export default TodoForm;

example/components/TodoList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ function TodoList() {
66
const todos = useTodosState();
77
return (
88
<ul>
9-
{todos.map(todo => (
9+
{todos?.root?.map(todo => (
1010
<TodoItem todo={todo} key={todo.id} />
1111
))}
1212
</ul>

example/contexts/TodosContext.tsx

Lines changed: 50 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,75 +6,87 @@ import React, {
66
useEffect,
77
} from 'react';
88

9-
import useContextDevTools from '../../index';
9+
import useContextDevTools from '../../index.min';
1010

1111
export type Todo = {
1212
id: number;
1313
text: string;
1414
done: boolean;
1515
};
1616

17-
export type TodosState = Todo[];
17+
export type TodosState = { root: Todo[] };
1818

1919
const TodosStateContext = createContext<TodosState | undefined>(undefined);
2020

2121
type Action =
2222
| { type: 'CREATE'; text: string }
2323
| { type: 'TOGGLE'; id: number }
24-
| { type: 'REMOVE'; id: number };
24+
| { type: 'REMOVE'; id: number }
25+
| { type: 'IMPORT_STATE'; state: any };
2526

2627
type TodosDispatch = Dispatch<Action>;
2728
const TodosDispatchContext = createContext<TodosDispatch | undefined>(
2829
undefined
2930
);
3031

31-
function todosReducer(state: TodosState, action: Action): TodosState {
32+
function todosReducer(givenState: TodosState, action: Action): TodosState {
33+
const state = givenState.root;
3234
switch (action.type) {
33-
case 'CREATE':
34-
const nextId = Math.max(...state.map(todo => todo.id)) + 1;
35-
return state.concat({
36-
id: nextId,
37-
text: action.text,
38-
done: false
39-
});
40-
case 'TOGGLE':
41-
return state.map(todo =>
42-
todo.id === action.id ? { ...todo, done: !todo.done } : todo
43-
);
44-
case 'REMOVE':
45-
return state.filter(todo => todo.id !== action.id);
46-
default:
47-
throw new Error('Unhandled action');
48-
}
35+
case 'CREATE':
36+
const nextId = Math.max(...state.map((todo) => todo.id)) + 1;
37+
return {
38+
root: state.concat({
39+
id: (Number.isFinite(nextId) && nextId) || 1,
40+
text: action.text,
41+
done: false,
42+
}),
43+
};
44+
case 'TOGGLE':
45+
return {
46+
root: state.map((todo) =>
47+
todo.id === action.id ? { ...todo, done: !todo.done } : todo
48+
)
49+
}
50+
case 'REMOVE':
51+
return {
52+
root: state.filter((todo) => todo.id !== action.id),
53+
};
54+
case 'IMPORT_STATE':
55+
return action.state;
56+
default:
57+
throw new Error('Unhandled action');
58+
}
4959
}
5060

5161
export function TodosContextProvider({
5262
children
5363
}: {
5464
children: React.ReactNode;
5565
}) {
56-
const [todos, dispatch] = useReducer(todosReducer, [
57-
{
58-
id: 1,
59-
text: 'Learn Context API',
60-
done: true,
61-
},
62-
{
63-
id: 2,
64-
text: 'Learn TypeScript',
65-
done: true,
66-
},
67-
{
68-
id: 3,
69-
text: 'Use Context API with TypeScript',
70-
done: false,
71-
},
72-
]);
66+
const [todos, dispatch] = useReducer(todosReducer, {
67+
root: [
68+
{
69+
id: 1,
70+
text: 'My first todo using context API',
71+
done: true,
72+
},
73+
{
74+
id: 2,
75+
text: 'Second one to try time travel debugging',
76+
done: true,
77+
},
78+
{
79+
id: 3,
80+
text: '3rd one to import new todos',
81+
done: false,
82+
},
83+
],
84+
});
7385

7486
const devTools = useContextDevTools(dispatch);
7587

7688
useEffect(() => {
77-
devTools.sendUpdatedState(todos);
89+
devTools.sendUpdatedState(todos);
7890
}, [todos, devTools]);
7991

8092

index.ts

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,46 @@
22
const devTools: any = { send: () => {} };
33
let isInitialized = false;
44
let firstRender = false;
5-
let oldArgs = {};
5+
let oldArgs: any = {};
66

77
const useContextDevTools = (dispatch: Function) => {
88
if (!isInitialized) {
9-
devTools.current = (window as any).__REDUX_DEVTOOLS_EXTENSION__.connect();
9+
devTools.current = (window as any).__REDUX_DEVTOOLS_EXTENSION__.connect({
10+
features: {
11+
pause: true, // start/pause recording of dispatched actions
12+
lock: true, // lock/unlock dispatching actions and side effects
13+
persist: true, // persist states on page reloading
14+
export: true, // export history of actions in a file
15+
import: 'custom', // import history of actions from a file
16+
jump: true, // jump back and forth (time travelling)
17+
skip: true, // skip (cancel) actions
18+
reorder: true, // drag and drop actions in the history list
19+
dispatch: true, // dispatch custom actions or action creators
20+
test: true // generate tests for the selected actions
21+
},
22+
// other options like actionSanitizer, stateSanitizer
23+
});
1024
isInitialized = true;
25+
devTools.current.subscribe((message: any) => {
26+
if (message.payload && (message.payload.type === 'JUMP_TO_STATE' || message.payload.type === 'JUMP_TO_ACTION') && message.state) {
27+
const parsedState = JSON.parse(message.state);
28+
sendDispatch({
29+
type: 'IMPORT_STATE',
30+
state: parsedState,
31+
});
32+
} else if (message.type === 'DISPATCH' && message.payload && message.payload.nextLiftedState) {
33+
message.payload.nextLiftedState.computedStates.forEach((cs: { state: any}, csi: number) => {
34+
const actionToSend = message.payload.nextLiftedState.actionsById[csi];
35+
if (actionToSend.action.type !== '@@INIT') {
36+
sendDispatch(actionToSend.action);
37+
}
38+
});
39+
}
40+
});
41+
window.addEventListener('beforeunload', function (e) {
42+
e.preventDefault();
43+
disconnectDevTools();
44+
});
1145
}
1246

1347
const sendDispatch = (args: any) => {
@@ -22,12 +56,14 @@ const useContextDevTools = (dispatch: Function) => {
2256
devTools.current.init(updatedState);
2357
firstRender = true;
2458
} else {
25-
devTools.current.send(oldArgs, { root: updatedState });
59+
if (oldArgs.type !== 'IMPORT_STATE') {
60+
devTools.current.send(oldArgs, updatedState);
61+
}
2662
}
2763
};
2864

2965
const disconnectDevTools = () => {
30-
return devTools.current.disconnect();
66+
return typeof devTools?.current?.disconnect === 'function' && devTools?.current?.disconnect();
3167
}
3268

3369
return {

0 commit comments

Comments
 (0)