[React] 18 - Project: Book Worm: Auth

前言


1、故事背景

Build App with Reactcss

Project系列,主要是從實踐的角度來分析需求,設計組件,設計網頁。html

 

 

2、組件設計綱要

 /* implement */react

 

 

 

代碼分析


1、項目配置問題

  • Eslint

.eslintrc文件採用airbnb的嚴格檢查模式。json

package.json中添加:redux

"lint": "eslint src"

 

  • 沒用的初始化默認文件

 

  • Function component

快捷鍵直接獲得以下代碼模板:api

 

  • 偷看顏色

點擊後出現顏色值提取器,以下:react-router

填下以下,這裏使用了 semanitic-ui。app

import React     from "react";
import PropTypes from "prop-types";

const InlineError = ({ text }) => (
  <span style={{ color: "#ae5856" }}>{text}</span>
);

# 參數檢查 InlineError.propTypes = { text: PropTypes.string.isRequired }; export default InlineError;

 

 

 

2、組件設計與實現

  • BrowserRouter

(1) This requires 「yarn add react-router-dom」dom

[index.js] ide

 

(2) 配置主頁 HomePage

 a. 打算 導入HomePage.js,並做爲路由根路徑。path = "/" exact

 b. 下一步,就是實現HomePage,至少包括了login的連接按鈕。

[HomePage.js]

 

(3) 實現主頁 HomePage,以及其中的 Link。

[HomePage.js]

 

(4) 實現 LoginPage

這裏主要是添加上對應的路由。

[App.js]

以上便實現了一個基本的路由過程。

 

爲何要用exact?

以下狀況下,若是匹配路由path='/page',那麼會把Home也會展現出來,這就是爲何要使用exact。

<Route path='/'     component={Home} />
<Route path='/page' component={Page}>

 

 

  • LoginPage

(1) semanitic-ui

Ref: https://react.semantic-ui.com/usage

安裝並導入:

$ yarn add semantic-ui-react
$ yarn add semantic-ui-css

import 'semantic-ui-css/semantic.min.css';

 className的值創建在semanitic-ui上,以後須要系統學習。

 

(2) LoginForm

render 的內容,首先得到須要的 value:

const { data, errors, loading } = this.state;  

而後渲染。

<Form.Field  ={!!errors.email}>
  <label htmlFor="email">Email</label>
  <input
    type       ="email"
    id         ="email"
    name       ="email"
    placeholder="example@example.com"
    value      ={data.email}
    onChange   ={this.onChange}
  />
  {errors.email && <InlineError text={errors.email} />}     # 判斷「有錯誤」纔出現提示
</Form.Field> <Form.Field error={!!errors.password}>   <label htmlFor="password">Password</label>   <input     type ="password"     id ="password"     name ="password"     placeholder="Make it secure"     value ={data.password}     onChange ={this.onChange}   />   {errors.password && <InlineError text={errors.password} />} </Form.Field>

格式錯誤後出現小提示。

 

(3) onChange

  onChange = e => {
 console.log("-->name " + e.target.name) console.log("-->value " + e.target.value)
    this.setState({
      data: { ...this.state.data, [e.target.name]: e.target.value }
    });
  }

a) 若是去掉...this.state.data會報警告以下:

b) 由於我是須要先總體覆蓋一次,也就是setState的第一個參數,而後再改變咱們想要改變的。

 

(4) onSubmit

首先要驗證下格式;而後再提交登陸。

 validate = data => {
    const errors = {};
    if (!Validator.isEmail(data.email)) errors.email = "Invalid email";
    if (!data.password) errors.password = "Can't be blank";
    return errors;
  };

提交操做的實現,以下:

[LoginForm.js]

  onSubmit = () => {
    const errors = this.validate(this.state.data);
this.setState({ errors });
if (Object.keys(errors).length === 0) {    // 格式沒問題的話 this.setState({ loading: true }); this.props.submit(this.state.data) .catch (err => this.setState({ errors: err.response.data.errors, loading: false }) ); } };

submit 真正的實現之地在外層。

[LoginPage.js]

class LoginPage extends React.Component {
  submit = data =>
    this.props.login(data).then(() => this.props.history.push("/dashboard"));
    /* 以後會調用到 ./actions/auth.js中的login */
render() { return (
<div> <h1>Login page</h1> <LoginForm submit={this.submit} /> <Link to="/forgot_password">Forgot Password?</Link> </div> ); } }

 

(5) error={!!errors.email}

雙重否認表示的意義是什麼? Ref: js雙感嘆號判斷

至關於三元運算符,返回boolean值。

var ret = !!document.getElementById
等價於:

var ret = document.getElementById ? true : false;

當值是非空字符串和非零數字返回true,當值是空字符串、0或者null返回false。

var a = " ";   alert(!!a);   //true
var a = "s";   alert(!!a);   //true
var a = true;  alert(!!a);   //true
var a = 1;     alert(!!a);   //true
var a = -1;    alert(!!a);   //true
var a = -2;    alert(!!a);   //true
 
var a = 0;     alert(!!a);   //false
var a = "";    alert(!!a);   //false
var a = false; alert(!!a);   //false
var a = null;  alert(!!a);   //false

 

 

 

3、Redux的應用

Ref: Build Real Web App with React #02

  • 導入相關庫

[index.js]

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Route } from "react-router-dom";
import "semantic-ui-css/semantic.min.css";
import { createStore, applyMiddleware } from "redux"; import { Provider } from "react-redux"; import thunk from "redux-thunk";
import decode from "jwt-decode";
import { composeWithDevTools } from "redux-devtools-extension";
import App from "./App";
import registerServiceWorker from "./registerServiceWorker";
import rootReducer from "./rootReducer";
import { userLoggedIn } from "./actions/auth";
import setAuthorizationHeader from "./utils/setAuthorizationHeader";

----------------------------------------------------------------------
const store = createStore( rootReducer,          # 樹!暫時先不搭建 ---> (2) composeWithDevTools(applyMiddleware(thunk))      # ---> (1) );
----------------------------------------------------------------------
if (localStorage.bookwormJWT) { const payload = decode(localStorage.bookwormJWT); const user = { token: localStorage.bookwormJWT, email: payload.email, confirmed: payload.confirmed }; setAuthorizationHeader(localStorage.bookwormJWT); store.dispatch(userLoggedIn(user)); } ReactDOM.render(
<BrowserRouter> <Providerstore={store}> <Route component={App} /> </Provider> </BrowserRouter>, document.getElementById("root") );
registerServiceWorker();

 

(1) 合併 sub-reducer。

[rootReducer.js]

import { combineReducers } from "redux";

import user  from "./reducers/user";
import books from "./reducers/books";

export default combineReducers({
  user,
  books
});

 

(2) 實時的監控Redux的狀態樹的Store

Ref: Redux-devTools簡單的使用

 

 

  • Redux 調用 Login by  connect(...) 

10:22 / 34:47

[LoginPage.js]

import { connect } from "react-redux";
import { Link }    from "react-router-dom";
import LoginForm   from "../forms/LoginForm";
import { login }   from "../../actions/auth";

class LoginPage extends React.Component {
  submit = data =>
    this.props.login(data).then(() => this.props.history.push("/dashboard"));
--------------------------------------------------------------------------------
render() { return (
<div> <h1>Login page</h1> <LoginForm submit={this.submit} /> <Link to="/forgot_password">Forgot Password?</Link> </div> ); } }

/**
* shape 相似於包裹着幾個屬性的 object
*/ LoginPage.propTypes = { history: PropTypes.shape({ push: PropTypes.func.isRequired }).isRequired, login: PropTypes.func.isRequired };

/**
* login 函數就放在了 LoginPage容器 裏面
*/ export default connect(null, { login })(LoginPage);

 

 

  • 實現 Login ---> Server

[actions/auth.js]

export const login = credentials => dispatch =>
  api.user.login(credentials).then(            # 真正幹活的地方
user => { localStorage.bookwormJWT = user.token; setAuthorizationHeader(user.token); dispatch(userLoggedIn(user)); }
);

這裏定義了動做信號。

export const userLoggedIn = user => ({
  type: USER_LOGGED_IN,
  user
});
相關文章
相關標籤/搜索