Skip to content

Commit

Permalink
Fix React hydration
Browse files Browse the repository at this point in the history
  • Loading branch information
Kreozot committed Sep 25, 2022
1 parent 7370d72 commit d9ca79d
Show file tree
Hide file tree
Showing 15 changed files with 134 additions and 105 deletions.
33 changes: 0 additions & 33 deletions gatsby-browser.js

This file was deleted.

27 changes: 27 additions & 0 deletions gatsby-browser.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* eslint-disable react/jsx-filename-extension */
/**
* Implement Gatsby's Browser APIs in this file.
*
* See: https://www.gatsbyjs.org/docs/browser-apis/
*/

// You can delete this file if you're not using it
import React from 'react';
import { WrapPageElementBrowserArgs, WrapRootElementBrowserArgs } from 'gatsby';
import { Provider } from 'react-redux';

import { store } from 'common/store';
import Layout from 'components/Layout';

export const wrapPageElement = ({ element, props }: WrapPageElementBrowserArgs) => {
// eslint-disable-next-line react/jsx-props-no-spreading
return <Layout {...props}>{element}</Layout>;
};

export const wrapRootElement = ({ element }: WrapRootElementBrowserArgs) => {
return (
<Provider store={store}>
{ element }
</Provider>
);
};
18 changes: 9 additions & 9 deletions gatsby-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ module.exports = {
icon: 'src/images/logo.png', // This path is relative to the root of the site.
},
},
// {
// resolve: 'gatsby-plugin-sentry',
// options: {
// dsn: 'https://[email protected]/5400964',
// // Optional settings, see https://docs.sentry.io/clients/node/config/#optional-settings
// environment: process.env.NODE_ENV,
// enabled: (() => ['production', 'stage'].indexOf(process.env.NODE_ENV) !== -1)(),
// },
// },
{
resolve: 'gatsby-plugin-sentry',
options: {
dsn: 'https://[email protected]/5400964',
// Optional settings, see https://docs.sentry.io/clients/node/config/#optional-settings
environment: process.env.NODE_ENV,
enabled: (() => ['production', 'stage'].indexOf(process.env.NODE_ENV) !== -1)(),
},
},
// this (optional) plugin enables Progressive Web App + Offline functionality
// To learn more, visit: https://gatsby.dev/offline
// TODO: Doesn't invalidate cache properly
Expand Down
20 changes: 0 additions & 20 deletions gatsby-ssr.js

This file was deleted.

27 changes: 27 additions & 0 deletions gatsby-ssr.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* eslint-disable react/jsx-filename-extension */
/**
* Implement Gatsby's SSR (Server Side Rendering) APIs in this file.
*
* See: https://www.gatsbyjs.org/docs/ssr-apis/
*/

// You can delete this file if you're not using it
import { WrapRootElementNodeArgs, WrapPageElementNodeArgs } from 'gatsby';
import React from 'react';
import { Provider } from 'react-redux';

import Layout from 'components/Layout';
import { store } from 'common/store';

export const wrapPageElement = ({ element, props }: WrapPageElementNodeArgs) => {
// eslint-disable-next-line react/jsx-props-no-spreading
return <Layout {...props}>{element}</Layout>;
};

export const wrapRootElement = ({ element }: WrapRootElementNodeArgs) => {
return (
<Provider store={store}>
{ element }
</Provider>
);
};
14 changes: 0 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@
"react-helmet": "^6.1.0",
"react-redux": "^8.0.2",
"react-table": "^7.8.0",
"redux-persist": "^6.0.0",
"reselect": "^4.1.6",
"sass": "^1.54.9",
"typograf": "^6.15.0",
Expand All @@ -73,7 +72,6 @@
"@types/react-dom": "^18.0.6",
"@types/react-helmet": "^6.1.5",
"@types/react-table": "^7.7.12",
"@types/redux-persist": "^4.3.1",
"@typescript-eslint/eslint-plugin": "^5.37.0",
"@typescript-eslint/parser": "^5.37.0",
"eslint": "^8.23.1",
Expand Down
7 changes: 6 additions & 1 deletion src/common/store/chosenSpellsSlice.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import without from 'lodash/without';

type ChosenSpellsSlice = {
export type ChosenSpellsSlice = {
spells: string[];
cantrips: string[];
};
Expand All @@ -13,6 +13,11 @@ export default createSlice({
cantrips: [],
} as ChosenSpellsSlice,
reducers: {
replaceState(state, action: PayloadAction<ChosenSpellsSlice>): ChosenSpellsSlice {
return {
...action.payload
};
},
toggleSpellChosen(state, action: PayloadAction<{ title: string, isSpellChosen: boolean }>): ChosenSpellsSlice {
const { title, isSpellChosen } = action.payload;
if (isSpellChosen) {
Expand Down
5 changes: 5 additions & 0 deletions src/common/store/filtersSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ export default createSlice({
spellcastingAbilityValue: undefined,
} as FiltersSlice,
reducers: {
replaceState(state, action: PayloadAction<FiltersSlice>): FiltersSlice {
return {
...action.payload
};
},
setSpellsFilter(state, action: PayloadAction<SpellsFilter>): FiltersSlice {
return {
...state,
Expand Down
22 changes: 10 additions & 12 deletions src/common/store/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { configureStore, combineReducers } from '@reduxjs/toolkit';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { createSelector } from 'reselect';
import throttle from 'lodash/throttle';

import spellsData from 'content/spells';
import classSpellsData from 'content/classSpells.yaml';
Expand All @@ -11,26 +10,27 @@ import sortBy from 'lodash/sortBy';
import chosenSpellsSlice from './chosenSpellsSlice';
import filtersSlice from './filtersSlice';
import spellsLevelsSlice from './spellsLevelsSlice';

const persistConfig = {
key: 'root',
storage,
blacklist: ['spellsLevels'],
};
import { saveState, StorageKey } from './localStorageState';

export { filtersSlice, chosenSpellsSlice, spellsLevelsSlice };

export const store = configureStore({
reducer: persistReducer(persistConfig, combineReducers({
reducer: combineReducers({
filters: filtersSlice.reducer,
chosenSpells: chosenSpellsSlice.reducer,
spellsLevels: spellsLevelsSlice.reducer,
})),
}),
middleware: [],
});
export type State = ReturnType<typeof store.getState>;
export type Dispatch = typeof store.dispatch;

store.subscribe(throttle(() => {
const { filters, chosenSpells } = store.getState();
saveState(StorageKey.FILTERS_STORAGE_KEY, filters);
saveState(StorageKey.CHOSEN_SPELLS_STORAGE_KEY, chosenSpells);
}, 1000));

/** Get key (name) of class additional options (e.g. "Divine Domains" for Cleric) */
export const getClassAdditionalKey = createSelector(
(state: State) => state.filters.class,
Expand Down Expand Up @@ -264,5 +264,3 @@ export const getSpellAttackModifier = createSelector(
return proficiencyBonus + abilityModifier;
}
);

export const persistor = persistStore(store);
23 changes: 23 additions & 0 deletions src/common/store/localStorageState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export enum StorageKey {
FILTERS_STORAGE_KEY = 'dnd_filters',
CHOSEN_SPELLS_STORAGE_KEY = 'dnd_chosen_spells'
}

export function loadState<T>(key: StorageKey) {
try {
const serializedState = localStorage.getItem(key);
if (!serializedState) return undefined;
return JSON.parse(serializedState) as T;
} catch (e) {
return undefined;
}
}

export function saveState<T>(key: StorageKey, state: T) {
try {
const serializedState = JSON.stringify(state);
localStorage.setItem(key, serializedState);
} catch (e) {
// Ignore
}
}
9 changes: 5 additions & 4 deletions src/components/Spells/SpellSlotsCount/SpellSlotsCount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import SpellSlotsCountItem from './SpellSlotsCountItem';

import * as styles from './SpellSlotsCount.module.scss';

const LevelFilterButton: FC<ReduxProps> = (props) => {
const SpellSlotsCount: FC<ReduxProps> = (props) => {
const {
currentLevelClassRestrictions,
} = props;
Expand All @@ -17,9 +17,10 @@ const LevelFilterButton: FC<ReduxProps> = (props) => {

const values = [
currentLevelClassRestrictions.cantrips
&& <SpellSlotsCountItem level="cantrip" count={currentLevelClassRestrictions.cantrips} />,
&& <SpellSlotsCountItem level="cantrip" count={currentLevelClassRestrictions.cantrips} key="cantrip" />,
...currentLevelClassRestrictions.spellSlots
.map((value, i) => <SpellSlotsCountItem level={i + 1} count={value} />)
// eslint-disable-next-line react/no-array-index-key
.map((value, i) => <SpellSlotsCountItem level={i + 1} count={value} key={i} />)
]
.filter((value) => value);

Expand All @@ -37,4 +38,4 @@ const mapStateToProps = (state: State) => ({
const connector = connect(mapStateToProps);
type ReduxProps = ConnectedProps<typeof connector>;

export default connector(LevelFilterButton);
export default connector(SpellSlotsCount);
6 changes: 2 additions & 4 deletions src/pages/404.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import React, { FC } from 'react';

import Layout from 'components/Layout';

const NotFoundPage: FC = () => (
<Layout>
<>
<h1>NOT FOUND</h1>
<p>You just hit a route that doesn&#39;t exist... the sadness.</p>
</Layout>
</>
);

export default NotFoundPage;
22 changes: 17 additions & 5 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import React, { FC } from 'react';
import React, { FC, useEffect } from 'react';

import Layout from 'components/Layout';
import Spells from 'components/Spells';
import { loadState, StorageKey } from 'common/store/localStorageState';
import filtersSlice, { FiltersSlice } from 'common/store/filtersSlice';
import chosenSpellsSlice, { ChosenSpellsSlice } from 'common/store/chosenSpellsSlice';
import { store } from 'common/store';

const IndexPage: FC = () => {
useEffect(() => {
const filtersSavedState = loadState<FiltersSlice>(StorageKey.FILTERS_STORAGE_KEY);
if (filtersSavedState) {
store.dispatch(filtersSlice.actions.replaceState(filtersSavedState));
}
const chosenSpellsSavedState = loadState<ChosenSpellsSlice>(StorageKey.CHOSEN_SPELLS_STORAGE_KEY);
if (chosenSpellsSavedState) {
store.dispatch(chosenSpellsSlice.actions.replaceState(chosenSpellsSavedState));
}
}, []);

return (
<Layout>
<Spells />
</Layout>
<Spells />
);
};

Expand Down
4 changes: 3 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@
"./*.js",
"./gatsby-node.ts",
"./gatsby-config.ts",
"./plugins/**/*"
"./plugins/**/*",
"gatsby-ssr.tsx",
"gatsby-browser.tsx"
]
}

0 comments on commit d9ca79d

Please sign in to comment.