Skip to content

Commit 986eec4

Browse files
authored
feat: Update PauseableReduxContainer to use redux-pauseable-store (#12)
* Update PauseableReduxContainer to use redux-pauseable-store * Update story for PauseableComponentContainer * Add story for PauseableReduxContainer * Expand and enhance stories for PauseableReduxContainer
1 parent 0f6f9b9 commit 986eec4

15 files changed

+335
-185
lines changed

packages/dev-helpers/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"@material-ui/core": "^4.9.4",
5454
"@testing-library/react": "^10.2.1",
5555
"history": "^4.10.1",
56+
"prop-types": "^15.7.2",
5657
"react-is": "^16.13.0",
5758
"react-redux": "^7.2.0",
5859
"react-router": "^5.1.2",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import PropTypes from 'prop-types';
2+
import React, { useRef } from 'react';
3+
4+
import Typography from '@material-ui/core/Typography';
5+
6+
interface RenderCountProps {
7+
prefix?: string;
8+
}
9+
10+
const RenderCount: React.FC<RenderCountProps> = (props) => {
11+
const { prefix = 'Render count: ' } = props;
12+
13+
const renderCountRef = useRef(0);
14+
renderCountRef.current++;
15+
16+
return (
17+
<Typography variant="body1">
18+
{prefix}
19+
{renderCountRef.current}
20+
</Typography>
21+
);
22+
};
23+
24+
RenderCount.propTypes = {
25+
prefix: PropTypes.string,
26+
};
27+
28+
export default RenderCount;

packages/dev-helpers/src/components/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ export * from './DemoContainer';
33

44
export { default as NestedState } from './NestedState';
55
export * from './NestedState';
6+
7+
export { default as RenderCount } from './RenderCount';
8+
export * from './RenderCount';
+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
export { default as reduxDecorator } from './reduxDecorator';
22
export * from './reduxDecorator';
33

4+
export { default as useCountSelector } from './useCountSelector';
5+
export * from './useCountSelector';
6+
47
export * from './store';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { useSelector } from 'react-redux';
2+
3+
import { DevHelperState } from './store';
4+
5+
const countSelector = (state: DevHelperState) => state.count;
6+
7+
const useCountSelector = (): number => {
8+
return useSelector(countSelector);
9+
};
10+
11+
export default useCountSelector;

packages/react-hibernate/package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@
5959
},
6060
"peerDependencies": {
6161
"react": ">=16.8.0",
62-
"react-dom": ">=16.8.0",
63-
"react-router": ">=5.0.0"
62+
"react-dom": ">=16.8.0"
6463
}
6564
}

packages/react-pauseable-containers/package.json

+5-3
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,12 @@
5050
"test:watch": "echo \"@TODO: tests for pauseable-containers\"",
5151
"types": "tsc --noEmit --p tsconfig.json --jsx react"
5252
},
53-
"dependencies": {},
53+
"dependencies": {
54+
"prop-types": "^15.7.2",
55+
"redux-pauseable-store": "0.0.3"
56+
},
5457
"devDependencies": {
55-
"react-hibernate-dev-helpers": "0.0.2",
56-
"react-router-hibernate": "0.0.2"
58+
"react-hibernate-dev-helpers": "0.0.2"
5759
},
5860
"peerDependencies": {
5961
"react": ">=16.8.0",

packages/react-pauseable-containers/src/PauseableComponentContainer.tsx

+1-5
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,7 @@ class PauseableComponentContainer extends React.Component<PauseableContainerProp
99
}
1010

1111
render(): ReactNode {
12-
const child = this.props.children;
13-
if (child === null || child === false) {
14-
return null;
15-
}
16-
return React.Children.only(child);
12+
return this.props.children;
1713
}
1814
}
1915

Original file line numberDiff line numberDiff line change
@@ -1,44 +1,43 @@
1-
import React, { PropsWithChildren, ReactElement } from 'react';
2-
import { Store } from 'redux';
1+
import PropTypes from 'prop-types';
2+
import React from 'react';
33
import { Provider, useStore } from 'react-redux';
44

5+
import { createPauseableStore, PauseableStoreInstance } from 'redux-pauseable-store';
6+
57
import { PauseableContainerProps } from './types';
68

7-
const PauseableReduxContainer: React.FC<PauseableContainerProps> = ({
8-
shouldUpdate,
9-
children,
10-
}: PropsWithChildren<PauseableContainerProps>): ReactElement | null => {
11-
const store = useStore();
12-
const staticStoreRef = React.useRef<Store>();
13-
const wasActiveRef = React.useRef<boolean>();
14-
15-
const stateWhenLastActive = React.useRef<Store>();
16-
17-
if (shouldUpdate) {
18-
// Track stuff for when we go inactive
19-
stateWhenLastActive.current = store.getState();
20-
} else {
21-
if (wasActiveRef.current) {
22-
// We're going inactive: freeze the store contents to the last-active state
23-
staticStoreRef.current = {
24-
...store,
25-
getState: (): ReturnType<typeof store.getState> => stateWhenLastActive.current,
26-
};
27-
} else {
28-
// We're somehow being rendered in an initially-inactive state: that can't be right
29-
if (process.env.NODE_ENV !== 'production') {
30-
console.warn(
31-
'PauseableReduxContainer is being mounted with shouldUpdate=false: this is probably a bug',
32-
);
33-
}
34-
return null;
35-
}
36-
}
37-
38-
wasActiveRef.current = shouldUpdate;
39-
return (
40-
<Provider store={shouldUpdate ? store : (staticStoreRef.current as Store)}>{children}</Provider>
9+
export interface PauseableReduxContainerProps extends PauseableContainerProps {
10+
children: React.ReactNode;
11+
dispatchWhenPaused?: boolean | null;
12+
}
13+
14+
const PauseableReduxContainer: React.FC<PauseableReduxContainerProps> = (props) => {
15+
const { dispatchWhenPaused, shouldUpdate, children } = props;
16+
17+
const parentStore = useStore();
18+
const pauseableStore = React.useMemo<PauseableStoreInstance>(
19+
() =>
20+
createPauseableStore(parentStore, {
21+
// A change to the `shouldUpdate` prop will already cause a rerender, so we don't need an extra notification
22+
notifyListersOnUnpause: false,
23+
}),
24+
[parentStore],
4125
);
26+
27+
pauseableStore.setPaused(!shouldUpdate);
28+
pauseableStore.setDispatch(dispatchWhenPaused);
29+
30+
return <Provider store={pauseableStore}>{children}</Provider>;
31+
};
32+
33+
PauseableReduxContainer.defaultProps = {
34+
dispatchWhenPaused: null,
35+
};
36+
37+
PauseableReduxContainer.propTypes = {
38+
children: PropTypes.node.isRequired,
39+
dispatchWhenPaused: PropTypes.bool,
40+
shouldUpdate: PropTypes.bool.isRequired,
4241
};
4342

4443
export default PauseableReduxContainer;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import PropTypes from 'prop-types';
2+
import React, { useState } from 'react';
3+
4+
import Checkbox from '@material-ui/core/Checkbox';
5+
import Chip from '@material-ui/core/Chip';
6+
import FormControlLabel from '@material-ui/core/FormControlLabel';
7+
import Paper from '@material-ui/core/Paper';
8+
import Typography from '@material-ui/core/Typography';
9+
10+
import { RenderCount } from 'react-hibernate-dev-helpers';
11+
12+
import { PauseableComponentContainer } from '../src';
13+
14+
export interface PauseableComponentItemProps {
15+
count: number;
16+
}
17+
18+
const PauseableComponentItem: React.FC<PauseableComponentItemProps> = (props) => {
19+
const { count } = props;
20+
21+
const [shouldUpdate, setShouldUpdate] = useState(true);
22+
23+
return (
24+
<Paper style={{ marginTop: 10, padding: 5 }}>
25+
<FormControlLabel
26+
control={
27+
<Checkbox
28+
checked={shouldUpdate}
29+
onChange={(event) => setShouldUpdate(event.target.checked)}
30+
/>
31+
}
32+
label="shouldUpdate"
33+
/>
34+
<div>
35+
<PauseableComponentContainer shouldUpdate={shouldUpdate}>
36+
<Typography variant="body1" component="div">
37+
count: <Chip label={count} />
38+
</Typography>
39+
<RenderCount />
40+
</PauseableComponentContainer>
41+
</div>
42+
</Paper>
43+
);
44+
};
45+
46+
PauseableComponentItem.propTypes = {
47+
count: PropTypes.number.isRequired,
48+
};
49+
50+
export default PauseableComponentItem;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import PropTypes from 'prop-types';
2+
import React, { useState } from 'react';
3+
4+
import Checkbox from '@material-ui/core/Checkbox';
5+
import Chip from '@material-ui/core/Chip';
6+
import FormControlLabel from '@material-ui/core/FormControlLabel';
7+
import Typography from '@material-ui/core/Typography';
8+
import Paper from '@material-ui/core/Paper';
9+
10+
import { RenderCount, useCountSelector } from 'react-hibernate-dev-helpers';
11+
12+
import { PauseableComponentContainer, PauseableReduxContainer } from '../src';
13+
14+
interface PauseableReduxItemProps {
15+
dispatchWhenPaused?: boolean;
16+
}
17+
18+
const PauseableReduxItem: React.FC<PauseableReduxItemProps> = (props) => {
19+
const { dispatchWhenPaused } = props;
20+
const count = useCountSelector();
21+
22+
const [shouldUpdate, setShouldUpdate] = useState(true);
23+
24+
return (
25+
<Paper style={{ marginTop: 10, padding: 5 }}>
26+
<FormControlLabel
27+
control={
28+
<Checkbox
29+
checked={shouldUpdate}
30+
onChange={(event) => setShouldUpdate(event.target.checked)}
31+
/>
32+
}
33+
label="shouldUpdate"
34+
/>
35+
<PauseableComponentContainer shouldUpdate={shouldUpdate}>
36+
<PauseableReduxContainer
37+
shouldUpdate={shouldUpdate}
38+
dispatchWhenPaused={dispatchWhenPaused}
39+
>
40+
<Typography variant="body1" component="div">
41+
count: <Chip label={count} />
42+
</Typography>
43+
<RenderCount />
44+
</PauseableReduxContainer>
45+
</PauseableComponentContainer>
46+
</Paper>
47+
);
48+
};
49+
50+
PauseableReduxItem.propTypes = {
51+
dispatchWhenPaused: PropTypes.bool,
52+
};
53+
54+
PauseableReduxItem.defaultProps = {
55+
dispatchWhenPaused: false,
56+
};
57+
58+
export default PauseableReduxItem;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import React, { useCallback } from 'react';
2+
3+
import Chip from '@material-ui/core/Chip';
4+
5+
import { incrementAction, useCountSelector } from 'react-hibernate-dev-helpers';
6+
7+
import Button from '@material-ui/core/Button';
8+
import { useDispatch } from 'react-redux';
9+
10+
const PauseableReduxItem: React.FC = () => {
11+
const dispatch = useDispatch();
12+
const count = useCountSelector();
13+
14+
const increment = useCallback(() => dispatch(incrementAction()), []);
15+
16+
return (
17+
<>
18+
<Button onClick={increment} variant="contained">
19+
Increment
20+
</Button>
21+
<div>
22+
Redux count:
23+
<Chip label={count} />
24+
</div>
25+
</>
26+
);
27+
};
28+
29+
export default PauseableReduxItem;

0 commit comments

Comments
 (0)