Add redux for global app state management
This commit is contained in:
parent
6d8985e441
commit
1a81c7ead5
@ -13,7 +13,9 @@
|
||||
"@types/node": "^12.0.0",
|
||||
"@types/react": "^16.9.0",
|
||||
"@types/react-dom": "^16.9.0",
|
||||
"@types/react-redux": "^7.1.9",
|
||||
"@types/react-router-dom": "^5.1.5",
|
||||
"@types/redux": "^3.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^2.10.0",
|
||||
"@typescript-eslint/parser": "^2.10.0",
|
||||
"babel-core": "^6.26.3",
|
||||
@ -38,7 +40,10 @@
|
||||
"react-app-polyfill": "^1.0.6",
|
||||
"react-dev-utils": "^10.2.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-redux": "^7.2.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"redux": "^4.0.5",
|
||||
"redux-devtools-extension": "^2.13.8",
|
||||
"sass-loader": "8.0.2",
|
||||
"style-loader": "0.23.1",
|
||||
"ts-loader": "^7.0.5",
|
||||
|
106
src/app.tsx
106
src/app.tsx
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { Dispatch } from 'react';
|
||||
import { MuiThemeProvider as ThemeProvider, StylesProvider } from "@material-ui/core/styles";
|
||||
import { studentTheme } from "./ui/theme";
|
||||
import { MuiPickersUtilsProvider } from "@material-ui/pickers";
|
||||
@ -11,7 +11,11 @@ import '@/styles/header.scss'
|
||||
|
||||
import moment, { Moment } from "moment";
|
||||
import { route, routes } from "@/routing";
|
||||
import { Button, Divider } from '@material-ui/core';
|
||||
import { Provider, useDispatch, useSelector } from "react-redux";
|
||||
import store from "@/state/store";
|
||||
import { AppState } from "@/state/reducer";
|
||||
import { StudentAction, StudentActions } from "@/state/actions/student";
|
||||
import { sampleStudent } from "@/provider/dummy/student";
|
||||
|
||||
moment.locale("pl")
|
||||
|
||||
@ -21,42 +25,72 @@ class LocalizedMomentUtils extends MomentUtils {
|
||||
}
|
||||
}
|
||||
|
||||
const UserMenu = () => {
|
||||
const student = useSelector<AppState>(state => state.student);
|
||||
|
||||
const dispatch = useDispatch<Dispatch<StudentAction>>();
|
||||
|
||||
const handleUserLogin = () => {
|
||||
dispatch({
|
||||
type: StudentActions.Login,
|
||||
student: sampleStudent,
|
||||
})
|
||||
}
|
||||
|
||||
const handleUserLogout = () => {
|
||||
dispatch({ type: StudentActions.Logout })
|
||||
}
|
||||
|
||||
return student ? <>
|
||||
zalogowany jako: <strong>Jan Kowalski</strong>
|
||||
{' '}
|
||||
(<Link to={'#'} onClick={ handleUserLogout }>wyloguj się</Link>)
|
||||
</> : <>
|
||||
<Link to={'#'} onClick={ handleUserLogin }>zaloguj się</Link>
|
||||
</>;
|
||||
}
|
||||
|
||||
const AppHeader = () => {
|
||||
return <header className="header">
|
||||
<div id="logo" className="header__logo">
|
||||
<Link to={ route('home') }>
|
||||
<img src="img/pg-logotyp.svg"/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="header__nav">
|
||||
<nav className="header__top">
|
||||
<ul className="header__menu"></ul>
|
||||
<div className="header__user">
|
||||
<UserMenu />
|
||||
</div>
|
||||
<div className="header__divider" />
|
||||
<ul className="header__language-switcher">
|
||||
<li>pl</li>
|
||||
<li>en</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<nav className="header__bottom">
|
||||
<ul className="header__menu header__menu--main"></ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
}
|
||||
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<StylesProvider injectFirst>
|
||||
<MuiPickersUtilsProvider utils={ LocalizedMomentUtils } libInstance={ moment }>
|
||||
<ThemeProvider theme={ studentTheme }>
|
||||
<BrowserRouter>
|
||||
<header className="header">
|
||||
<div id="logo" className="header__logo">
|
||||
<Link to={ route('home') }>
|
||||
<img src="img/eti-logo.svg"/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="header__nav">
|
||||
<nav className="header__top">
|
||||
<ul className="header__menu"></ul>
|
||||
<div className="header__user">
|
||||
zalogowany jako: <strong>Jan Kowalski</strong>
|
||||
{' '}
|
||||
(<Link to={'#'}>wyloguj się</Link>)
|
||||
</div>
|
||||
<div className="header__divider" />
|
||||
<ul className="header__language-switcher">
|
||||
<li>pl</li>
|
||||
<li>en</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<nav className="header__bottom">
|
||||
<ul className="header__menu header__menu--main"></ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
<Switch>{ routes.map(({ name, content, ...route }) => <Route { ...route } key={ name }>{ content() }</Route>) }</Switch>
|
||||
</BrowserRouter>
|
||||
</ThemeProvider>
|
||||
</MuiPickersUtilsProvider>
|
||||
</StylesProvider>
|
||||
<Provider store={ store }>
|
||||
<StylesProvider injectFirst>
|
||||
<MuiPickersUtilsProvider utils={ LocalizedMomentUtils } libInstance={ moment }>
|
||||
<ThemeProvider theme={ studentTheme }>
|
||||
<BrowserRouter>
|
||||
<AppHeader />
|
||||
<Switch>{ routes.map(({ name, content, ...route }) => <Route { ...route } key={ name }>{ content() }</Route>) }</Switch>
|
||||
</BrowserRouter>
|
||||
</ThemeProvider>
|
||||
</MuiPickersUtilsProvider>
|
||||
</StylesProvider>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
||||
|
3
src/state/actions/base.ts
Normal file
3
src/state/actions/base.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export interface Action<TType extends string> {
|
||||
readonly type: TType;
|
||||
}
|
0
src/state/actions/index.ts
Normal file
0
src/state/actions/index.ts
Normal file
16
src/state/actions/student.ts
Normal file
16
src/state/actions/student.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { Action } from "@/state/actions/base";
|
||||
import { Student } from "@/data";
|
||||
|
||||
export enum StudentActions {
|
||||
Login = 'LOGIN',
|
||||
Logout = 'LOGOUT'
|
||||
}
|
||||
|
||||
export interface LoginAction extends Action<StudentActions.Login> {
|
||||
student: Student
|
||||
}
|
||||
|
||||
export type LogoutAction = Action<StudentActions.Logout>;
|
||||
|
||||
export type StudentAction = LoginAction | LogoutAction;
|
||||
|
10
src/state/reducer/index.ts
Normal file
10
src/state/reducer/index.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { combineReducers } from "redux";
|
||||
import studentReducer from "./student"
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
student: studentReducer,
|
||||
})
|
||||
|
||||
export type AppState = ReturnType<typeof rootReducer>;
|
||||
|
||||
export default rootReducer;
|
20
src/state/reducer/student.ts
Normal file
20
src/state/reducer/student.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { Student } from "@/data";
|
||||
import { StudentAction, StudentActions } from "@/state/actions/student";
|
||||
|
||||
export type StudentState = Student | null;
|
||||
|
||||
const initialStudentState: StudentState = null;
|
||||
|
||||
const studentReducer = (state: StudentState = initialStudentState, action: StudentAction): StudentState => {
|
||||
switch (action.type) {
|
||||
case StudentActions.Login:
|
||||
return action.student;
|
||||
|
||||
case StudentActions.Logout:
|
||||
return null;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
export default studentReducer;
|
7
src/state/store.ts
Normal file
7
src/state/store.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { createStore } from "redux";
|
||||
import rootReducer from "@/state/reducer";
|
||||
import { devToolsEnhancer } from "redux-devtools-extension";
|
||||
|
||||
const store = createStore(rootReducer, devToolsEnhancer({ }));
|
||||
|
||||
export default store;
|
58
yarn.lock
58
yarn.lock
@ -1146,6 +1146,14 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.6.tgz#ed8fc802c45b8e8f54419c2d054e55c9ea344356"
|
||||
integrity sha512-GRTZLeLJ8ia00ZH8mxMO8t0aC9M1N9bN461Z2eaRurJo6Fpa+utgCwLzI4jQHcrdzuzp5WPN9jRwpsCQ1VhJ5w==
|
||||
|
||||
"@types/hoist-non-react-statics@^3.3.0":
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
|
||||
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
|
||||
"@types/json-schema@^7.0.3", "@types/json-schema@^7.0.4":
|
||||
version "7.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd"
|
||||
@ -1188,6 +1196,16 @@
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-redux@^7.1.9":
|
||||
version "7.1.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.9.tgz#280c13565c9f13ceb727ec21e767abe0e9b4aec3"
|
||||
integrity sha512-mpC0jqxhP4mhmOl3P4ipRsgTgbNofMRXJb08Ms6gekViLj61v1hOZEKWDCyWsdONr6EjEA6ZHXC446wdywDe0w==
|
||||
dependencies:
|
||||
"@types/hoist-non-react-statics" "^3.3.0"
|
||||
"@types/react" "*"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
redux "^4.0.0"
|
||||
|
||||
"@types/react-router-dom@^5.1.5":
|
||||
version "5.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.5.tgz#7c334a2ea785dbad2b2dcdd83d2cf3d9973da090"
|
||||
@ -1220,6 +1238,13 @@
|
||||
"@types/prop-types" "*"
|
||||
csstype "^2.2.0"
|
||||
|
||||
"@types/redux@^3.6.0":
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/redux/-/redux-3.6.0.tgz#f1ebe1e5411518072e4fdfca5c76e16e74c1399a"
|
||||
integrity sha1-8evh5UEVGAcuT9/KXHbhbnTBOZo=
|
||||
dependencies:
|
||||
redux "*"
|
||||
|
||||
"@types/source-list-map@*":
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
|
||||
@ -4293,7 +4318,7 @@ hmac-drbg@^1.0.0:
|
||||
minimalistic-assert "^1.0.0"
|
||||
minimalistic-crypto-utils "^1.0.1"
|
||||
|
||||
hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.2:
|
||||
hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
||||
@ -7298,11 +7323,22 @@ react-error-overlay@^6.0.7:
|
||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108"
|
||||
integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA==
|
||||
|
||||
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1:
|
||||
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.9.0:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
||||
react-redux@^7.2.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d"
|
||||
integrity sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.7.2"
|
||||
react-is "^16.9.0"
|
||||
|
||||
react-router-dom@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662"
|
||||
@ -7421,6 +7457,19 @@ redent@^1.0.0:
|
||||
indent-string "^2.1.0"
|
||||
strip-indent "^1.0.1"
|
||||
|
||||
redux-devtools-extension@^2.13.8:
|
||||
version "2.13.8"
|
||||
resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz#37b982688626e5e4993ff87220c9bbb7cd2d96e1"
|
||||
integrity sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg==
|
||||
|
||||
redux@*, redux@^4.0.0, redux@^4.0.5:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
|
||||
integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
|
||||
dependencies:
|
||||
loose-envify "^1.4.0"
|
||||
symbol-observable "^1.2.0"
|
||||
|
||||
regenerate-unicode-properties@^8.2.0:
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec"
|
||||
@ -8405,6 +8454,11 @@ svgo@^1.0.0:
|
||||
unquote "~1.1.1"
|
||||
util.promisify "~1.0.0"
|
||||
|
||||
symbol-observable@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
||||
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
|
||||
|
||||
tapable@^1.0.0, tapable@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
|
||||
|
Loading…
Reference in New Issue
Block a user