Skip to content

Experiment: Redux with useMutableSource #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions __tests__/all_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const port = process.env.PORT || '8080';

const names = [
'react-redux',
'redux-use-mutable-source',
'reactive-react-redux',
'react-tracked',
'constate',
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"dev-server": "cross-env PORT=8080 webpack-dev-server --mode=development",
"http-server": "cross-env PORT=8080 http-server dist",
"build:react-redux": "cross-env NAME=react-redux webpack",
"build:redux-use-mutable-source": "cross-env NAME=redux-use-mutable-source webpack",
"build:reactive-react-redux": "cross-env NAME=reactive-react-redux webpack",
"build:react-tracked": "cross-env NAME=react-tracked webpack",
"build:constate": "cross-env NAME=constate webpack",
Expand Down
92 changes: 92 additions & 0 deletions src/redux-use-mutable-source/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React, {
useTransition,
createContext,
useCallback,
useContext,
useLayoutEffect,
useMemo,
useRef,
createMutableSource,
useMutableSource,
} from 'react';
import { createStore } from 'redux';

import {
syncBlock,
useRegisterIncrementDispatcher,
reducer,
ids,
useCheckTearing,
} from '../common';

const StoreContext = createContext();
const subscribe = (store, callback) => store.subscribe(callback);
const Provider = ({ store, children }) => {
const contextValue = useMemo(() => ({
source: createMutableSource(store, () => store.getState()),
dispatch: store.dispatch,
}), [store]);
return (
<StoreContext.Provider value={contextValue}>
{children}
</StoreContext.Provider>
);
};
const useDispatch = () => useContext(StoreContext).dispatch;
const useSelector = (selector) => {
const selectorRef = useRef(selector);
useLayoutEffect(() => {
selectorRef.current = selector;
});
const getSnapshot = useCallback((store) => selectorRef.current(store.getState()), []);
const { source } = useContext(StoreContext);
return useMutableSource(source, getSnapshot, subscribe);
};

const store = createStore(reducer);

const Counter = React.memo(() => {
const count = useSelector((state) => state.count);
syncBlock();
return <div className="count">{count}</div>;
}, () => true);

const Main = () => {
const dispatch = useDispatch();
const count = useSelector((state) => state.count);
useCheckTearing();
useRegisterIncrementDispatcher(React.useCallback(() => {
dispatch({ type: 'increment' });
}, [dispatch]));
const [localCount, localIncrement] = React.useReducer((c) => c + 1, 0);
const normalIncrement = () => {
dispatch({ type: 'increment' });
};
const [startTransition, isPending] = useTransition();
const transitionIncrement = () => {
startTransition(() => {
dispatch({ type: 'increment' });
});
};
return (
<div>
<button type="button" id="normalIncrement" onClick={normalIncrement}>Increment shared count normally (two clicks to increment one)</button>
<button type="button" id="transitionIncrement" onClick={transitionIncrement}>Increment shared count in transition (two clicks to increment one)</button>
<span id="pending">{isPending && 'Pending...'}</span>
<h1>Shared Count</h1>
{ids.map((id) => <Counter key={id} />)}
<div className="count">{count}</div>
<h1>Local Count</h1>
{localCount}
<button type="button" id="localIncrement" onClick={localIncrement}>Increment local count</button>
</div>
);
};

const App = () => (
<Provider store={store}>
<Main />
</Provider>
);

export default App;