refactor: state++

This commit is contained in:
cha0s 2020-07-26 17:31:36 -05:00
parent eb46f3a591
commit 785ab39bbd
7 changed files with 105 additions and 65 deletions

View File

@ -1,27 +1,14 @@
import merge from 'deepmerge';
import {combineReducers} from 'redux';
import {connectRouter, routerMiddleware} from 'connected-react-router';
import {routerMiddleware} from 'connected-react-router';
import {stateReducer, subscription} from '~/common/state';
import app from '~/common/state/app';
import chat from '~/common/state/chat';
import createHistory from '~/common/state/history';
import {storageSubscription} from '~/common/state/storage';
import user from '~/common/state/user';
import usernames from '~/common/state/usernames';
import createCommonStore from '~/common/store';
import {middleware as effectsMiddleware} from './effects';
export default function createStore(options = {}) {
const {history} = options;
const reducer = combineReducers({
app,
chat,
history: createHistory(history),
router: connectRouter(history),
user,
usernames,
});
const store = createCommonStore(
merge(
options,
@ -30,10 +17,10 @@ export default function createStore(options = {}) {
routerMiddleware(history),
effectsMiddleware,
],
reducer,
reducer: stateReducer(history),
},
),
);
store.subscribe(storageSubscription(store));
store.subscribe(subscription(store));
return store;
}

View File

@ -25,12 +25,13 @@ export const rightIsOpenSelector = createSelector(
);
const slice = createSlice({
name: 'app',
initialState: storage(appSelector) || {
name: 'reddichat/app',
initialState: {
leftActiveIndex: 0,
leftIsOpen: true,
rightActiveIndex: 0,
rightIsOpen: true,
...(storage(appSelector) || {}),
},
/* eslint-disable no-param-reassign */
extraReducers: {},

View File

@ -1,11 +1,10 @@
/* eslint-disable no-param-reassign */
import {
createSelector,
createSlice,
} from '@reduxjs/toolkit';
import hydration from './hydration';
import storage from './storage';
import storage, {localStorage} from './storage';
export const chatSelector = (state) => state.chat;
@ -45,7 +44,7 @@ export const channelMessagesSelector = createSelector(
);
const slice = createSlice({
name: 'chat',
name: 'reddichat/chat',
initialState: {
channels: {},
input: {},
@ -53,6 +52,10 @@ const slice = createSlice({
...(hydration('chat') || {}),
...(storage(chatSelector) || {}),
},
/* eslint-disable no-param-reassign */
extraReducers: {
[localStorage]: ({input}) => ({input}),
},
reducers: {
addMessage: ({
channels,
@ -117,6 +120,7 @@ const slice = createSlice({
submitLeave: () => {},
submitMessage: () => {},
},
/* eslint-enable no-param-reassign */
});
export const {

27
src/common/state/index.js Normal file
View File

@ -0,0 +1,27 @@
import {connectRouter} from 'connected-react-router';
import {combineReducers} from 'redux';
import app from './app';
import chat from './chat';
import createHistory from './history';
import {storageSubscription} from './storage';
import user from './user';
import usernames from './usernames';
export const stateReducer = (history) => combineReducers({
app,
chat,
history: createHistory(history),
router: connectRouter(history),
user,
usernames,
});
const subscriptionReducer = combineReducers({
app,
chat,
user,
usernames,
});
export const subscription = (store) => storageSubscription(store, subscriptionReducer);

View File

@ -1,11 +1,22 @@
import throttle from 'lodash.throttle';
import {createAction} from '@reduxjs/toolkit';
export const storageSubscription = (store) => (
throttle(() => localStorage.setItem('redux-state', JSON.stringify(store.getState())), 1000)
export const localStorage = createAction('reddichat/localStorage');
export const storageSubscription = (store, reducer) => (
throttle(
() => (
window.localStorage.setItem(
'redux-state',
JSON.stringify(reducer(store.getState(), localStorage())),
)
),
1000,
)
);
export default (selector) => {
const state = localStorage.getItem('redux-state');
const state = window.localStorage.getItem('redux-state');
if (!state) {
return undefined;
}

View File

@ -1,4 +1,5 @@
import {LOCATION_CHANGE} from 'connected-react-router';
import merge from 'deepmerge';
import {
createSelector,
createSlice,
@ -7,6 +8,7 @@ import {
import {addMessage, join, leave} from './chat';
import hydration from './hydration';
import storage, {localStorage} from './storage';
export const userSelector = (state) => state.user;
@ -104,8 +106,9 @@ export const unreadForChannelSelector = createSelector(
);
const slice = createSlice({
name: 'user',
initialState: {
name: 'reddichat/user',
initialState: merge.all([
{
blocked: [],
favorites: [],
focus: '',
@ -115,9 +118,34 @@ const slice = createSlice({
recent: [],
redditUsername: 'anonymous',
unread: {},
...(hydration('user') || {isAnonymous: true}),
},
hydration('user') || {isAnonymous: true},
storage(userSelector) || {},
], {arrayMerge: (target, source) => Array.from(new Set(source.concat(target)).values())}),
/* eslint-disable no-param-reassign */
extraReducers: {
[addMessage]: ({focus, unread}, {payload: {channel}}) => {
if (focus !== channel) {
unread[channel] = (unread[channel] || 0) + 1;
}
},
[join]: ({recent}, {payload: {channel}}) => {
if (-1 === recent.indexOf(channel)) {
recent.push(channel.substr(3));
}
},
[leave]: ({unread}, {payload: {channel}}) => {
delete unread[channel];
},
[localStorage]: ({recent}) => ({recent}),
[LOCATION_CHANGE]: (state, {payload: {location: {pathname}}}) => {
const {unread} = state;
state.focus = pathname.match(/^\/chat\//) ? pathname.substr('/chat'.length) : '';
if (unread[state.focus]) {
delete unread[state.focus];
}
},
},
reducers: {
addFriendship: ({friendship}, {payload}) => {
const index = friendshipIndex(friendship, payload);
@ -156,28 +184,6 @@ const slice = createSlice({
blocked.splice(blocked.indexOf(payload), 1);
},
},
extraReducers: {
[addMessage]: ({focus, unread}, {payload: {channel}}) => {
if (focus !== channel) {
unread[channel] = (unread[channel] || 0) + 1;
}
},
[join]: ({recent}, {payload: {channel}}) => {
if (-1 === recent.indexOf(channel) && channel.charCodeAt(1) === 'r'.charCodeAt(0)) {
recent.push(channel.substr(3));
}
},
[leave]: ({unread}, {payload: {channel}}) => {
delete unread[channel];
},
[LOCATION_CHANGE]: (state, {payload: {location: {pathname}}}) => {
const {unread} = state;
state.focus = pathname.match(/^\/chat\//) ? pathname.substr('/chat'.length) : '';
if (unread[state.focus]) {
delete unread[state.focus];
}
},
},
/* eslint-enable no-param-reassign */
});

View File

@ -9,6 +9,7 @@ import {
} from '~/common/state/chat';
import hydration from './hydration';
import storage from './storage';
export const usernamesSelector = (state) => state.usernames;
@ -17,10 +18,13 @@ export const usernameSelector = createSelector(
(usernames, id) => usernames[id],
);
/* eslint-disable no-param-reassign */
const slice = createSlice({
name: 'usernames',
initialState: hydration('usernames') || {},
name: 'reddichat/usernames',
initialState: {
...(hydration('usernames') || {}),
...(storage(usernamesSelector) || {}),
},
/* eslint-disable no-param-reassign */
extraReducers: {
[join]: (state, {payload: {usernames}}) => ({...state, ...usernames}),
[joined]: (state, {payload: {id, name}}) => ({...state, [id]: name}),
@ -28,8 +32,8 @@ const slice = createSlice({
reducers: {
setUsernames: (state, {payload}) => ({...state, ...payload}),
},
});
/* eslint-enable no-param-reassign */
});
export const {
setUsernames,