From fafefe7cd0cbf50462bc91eec728418b0c3c4ecd Mon Sep 17 00:00:00 2001 From: ShinCurry Date: Wed, 8 Jul 2020 13:49:58 +0800 Subject: [PATCH] recoil store --- .eslintrc.js | 6 ++++- package.json | 1 + src/App.tsx | 7 +++++- src/pages/Login.tsx | 16 ++++++++++++-- src/routers/Routes.tsx | 9 ++++---- src/store/RecoilContainer.tsx | 41 +++++++++++++++++++++++++++++++++++ src/store/index.ts | 18 +++++++++++++++ src/types/User.ts | 10 +++++++++ yarn.lock | 5 +++++ 9 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 src/store/RecoilContainer.tsx create mode 100644 src/store/index.ts create mode 100644 src/types/User.ts diff --git a/.eslintrc.js b/.eslintrc.js index 6065ae3..4bd562c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -32,8 +32,12 @@ module.exports = { "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-empty-interface": "off", + "@typescript-eslint/camelcase": "off", + "no-empty": "off", "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "warn", + "react-hooks/exhaustive-deps": ["warn", { + "additionalHooks": "useRecoilCallback" + }], "react/display-name": "off" }, "settings": { diff --git a/package.json b/package.json index 793ba11..57420d4 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "react-dom": "^16.13.1", "react-router-dom": "^5.2.0", "react-scripts": "3.4.1", + "recoil": "^0.0.10", "reflect-metadata": "^0.1.13", "typescript": "^3.9.6" }, diff --git a/src/App.tsx b/src/App.tsx index b123314..12aa36e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,11 +1,16 @@ import React from 'react'; import './App.css'; import { AppRouter } from './routers/AppRouter'; +import { store } from './store'; +import { RecoilContainer } from './store/RecoilContainer'; function App() { + return (
- + + +
); } diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index a49ba71..c610d04 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -1,12 +1,24 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { TextField, Button } from '@hackplan/uui'; import { useInject } from '../hooks/useInject'; import { AuthApi } from '../api/AuthApi'; +import { useRecoilState } from 'recoil'; +import { store } from '../store'; +import useRouter from '../hooks/useRouter'; export function Login() { + const { history } = useRouter() + const [username, setUsername] = useState('uui-template') const [password, setPassword] = useState('password') + const [auth, setAuth] = useRecoilState(store.Auth) + useEffect(() => { + if (auth && auth.token && auth.user) { + history.push('/') + } + }, [auth, history]) + const authApi = useInject(AuthApi) return ( @@ -24,7 +36,7 @@ export function Login() { diff --git a/src/routers/Routes.tsx b/src/routers/Routes.tsx index 85178dd..ecdab24 100644 --- a/src/routers/Routes.tsx +++ b/src/routers/Routes.tsx @@ -1,11 +1,13 @@ import React from 'react'; -import { Route, RouteProps, Redirect } from 'react-router-dom'; +import { Redirect, Route, RouteProps } from 'react-router-dom'; +import { useRecoilValue } from 'recoil'; +import { store } from '../store'; export const PublicRoute = Route; export const AuthenticatedRoute = (props: RouteProps) => { - // TODO: implement authenticated check - if (false) { + const isLogin = useRecoilValue(store.isLogin) + if (!isLogin) { return ( { /> ) } - return ( ) diff --git a/src/store/RecoilContainer.tsx b/src/store/RecoilContainer.tsx new file mode 100644 index 0000000..33284b4 --- /dev/null +++ b/src/store/RecoilContainer.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { RecoilRoot, useRecoilTransactionObserver_UNSTABLE, RecoilState } from 'recoil'; + +interface RecoilPersistProps { + states: RecoilState[]; +} +function RecoilPersist(props: RecoilPersistProps) { + useRecoilTransactionObserver_UNSTABLE(({ snapshot }) => { + for (const state of props.states) { + const { contents } = snapshot.getLoadable(state) + localStorage.setItem(state.key, JSON.stringify(contents)) + } + }) + return null +} + +export interface RecoilContainerProps { + children: React.ReactNode; + persistStates: RecoilPersistProps['states']; +} +export function RecoilContainer(props: RecoilContainerProps) { + + return ( + { + for (const persistState of props.persistStates) { + try { + const data = localStorage.getItem(persistState.key) + if (!data) continue + const state = JSON.parse(data) + set(persistState, state) + } catch (error) { + continue + } + } + }}> + + {props.children} + + ) +} + diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 0000000..0db4231 --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,18 @@ +import { atom, selector } from 'recoil'; +import { UserAuth } from '../types/User'; + +const Auth = atom({ + key: 'Auth', + default: null, +}) +const isLogin = selector({ + key: 'isLogin', + get: ({ get }) => { + const auth = get(Auth) + return !!auth && !!auth.user && !!auth.token + } +}) + +export const store = { + Auth, isLogin, +} diff --git a/src/types/User.ts b/src/types/User.ts new file mode 100644 index 0000000..45c818b --- /dev/null +++ b/src/types/User.ts @@ -0,0 +1,10 @@ +export interface User { + name: string; + avatar: string; + email: string; +} + +export interface UserAuth { + token: string; + user: User; +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 84219ae..1a6ed66 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9356,6 +9356,11 @@ realpath-native@^1.1.0: dependencies: util.promisify "^1.0.0" +recoil@^0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/recoil/-/recoil-0.0.10.tgz#679ab22306f559f8a63c46fd5ff5241539f9248f" + integrity sha512-+9gRqehw3yKETmoZbhSnWu4GO10HDb5xYf1CjLF1oXGK2uT6GX5Lu9mfTXwjxV/jXxEKx8MIRUUbgPxvbJ8SEw== + recursive-readdir@2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f"