Skip to content

Instantly share code, notes, and snippets.

@rossnelson
Created July 22, 2023 00:48
Show Gist options
  • Save rossnelson/329d2eaec071bea627f969fa4b442840 to your computer and use it in GitHub Desktop.
Save rossnelson/329d2eaec071bea627f969fa4b442840 to your computer and use it in GitHub Desktop.
React Context Api Abstraction
import React, { createContext, useContext, useReducer } from 'react';
import Log from 'lib/log';
import _ from 'lodash';
import stores from './stores';
const store = createContext(stores.state);
const { Provider } = store;
const StateProvider = ({ children }) => {
const initialState = {};
const storeNames = Object.keys(stores);
storeNames.forEach(name => {
initialState[name] = stores[name].state;
});
const [s, dispatch] = useReducer((state, action) => {
try {
const { store: storeName, reducer, value } = action;
const reducerMethod = _.get(stores, `${storeName}.reducers.${reducer}`);
const partialState = _.get(state, storeName);
const newPartialState = reducerMethod(partialState, value);
const newState = { ...state };
_.set(newState, storeName, newPartialState);
return newState;
} catch (err) {
Log.error(err, state, action);
return state;
}
}, initialState);
return <Provider value={{ state: s, dispatch }}>{children}</Provider>;
};
function useStore() {
const { state, dispatch } = useContext(store);
const actions = {};
const storeNames = Object.keys(stores);
storeNames.forEach(name => {
const storeActions = {};
const actionNames = Object.keys(stores[name].reducers);
actionNames.forEach(actionName => {
storeActions[actionName] = value => {
dispatch({
store: name,
reducer: actionName,
value
});
};
});
actions[name] = storeActions;
});
return { state, actions };
}
export { store, useStore, StateProvider };
import account from './account';
import impersonate from './impersonate';
import layout from './layout';
const stores = {
impersonate,
layout,
account
};
export default stores;
import { enUS } from '@mui/material/locale';
const store = {
state: {
menuOpen: false,
alert: {},
isWaiting: null,
locale: enUS,
section: null
},
reducers: {
setWaiting(state, isWaiting) {
return { ...state, isWaiting };
},
setLocale(state, locale) {
return { ...state, locale };
},
setAlert(state, alert = {}) {
return { ...state, alert };
},
setMenu(state, menuOpen) {
return { ...state, menuOpen };
}
}
};
export default store;
import { useEffect, useState } from 'react';
import { Navigate, useLocation } from 'react-router-dom';
import CenteredContent from 'lib/centered-content';
import Session from 'lib/session';
import { useStore } from 'store';
const Protection = ({ children }) => {
const { actions } = useStore();
const { pathname } = useLocation();
const [loading, setLoading] = useState(true);
const [isValid, setValid] = useState(false);
const ifValid = valid => {
actions.account.setAccount(Session.account);
setValid(valid);
setLoading(false);
};
useEffect(() => {
Session.validate(ifValid);
}, [pathname]); // eslint-disable-line
if (isValid) {
return children;
}
if (loading) {
return <CenteredContent />;
}
const to = {
pathname: '/auth/login',
state: {
returnurl: pathname
}
};
return <Navigate to={to} />;
};
export default function ProtectedRoute({ component: Component }) {
return (
<Protection>
<Component />
</Protection>
);
}
//import { Link } from 'react-router-dom';
import Container from '@mui/material/Container';
import { ReactComponent as Logo } from 'assets/images/logo.svg';
import Icon from 'lib/icon';
import { useStore } from 'store';
import Right from './right';
import styles from './styles.module.scss';
function TopBar() {
const {
state: {
layout: { menuOpen }
},
actions: { layout }
} = useStore();
return (
<div className={styles.topbar}>
<Container fixed>
<div className={styles.appleft}>
<Icon
className={styles.menutoggle}
onClick={() => layout.setMenu(!menuOpen)}
icon={['far', 'bars']}
/>
<a className={styles.logolink} href="/">
<Logo className={styles.logo} />
</a>
</div>
<Right />
</Container>
</div>
);
}
export default TopBar;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment