《reactHook+Typescript 從入門到實踐》年末讓這篇文章帶你飛

@本文章做者 Ruoduan.cn 轉載請註明出處html

  • 緣起

    公司中臺系統要新增公司項目 —— IM即時聊天 的後臺管理,由本人全程負責,前端技術選型 想採用目前比較新比較hot的技術 —— reactHook + react-Router5 + Typescript + (mobx | useContext + useState), 至於括號內的稍後解答,我的以爲能夠必定程度上替代 mobx 和 redux 的狀態管理前端

  • 本文將會從 Typescript,React-Hook,reactHook.TSX(實踐)這三個方面來敘述node

    • Typescriptreact

      • 項目搭建
      • 類型定義
      • 應用分析
      • ...
    • React-Hook ⭐️jquery

      • useState
      • useEffect
      • useContext
      • ...
    • Hook 與 mobx redux 等,實現本身狀態管理ios

    • React 全家桶的東西不詳細講解帶過 ...git


好了,下面讓咱們開始吧github

Typescript

React-Typescript 項目搭建

nodejs環境需具有 Not 下載連接typescript

咱們採用腳手架create-react-app 直接搭建react-typescript項目環境npm

版本要求 nodejs 8+ , Yarn 0.25+

npx create-react-app my-app --typescript

npm init react-app my-app --typescript

yarn create react-app my-app --typescript
複製代碼

Typescript 項目文件

  • 這裏講解一下typescript的一些文件定義
./
/src
    ./react-app-env.d.ts // 全局聲明文件

tsconfig.json // typescript 配置文件
複製代碼

tsconfig.json詳解 這裏例舉🌰幾個 :

{
  "compilerOptions": {
      "experimentalDecorators": true,
    "target": "es5", //語言編譯目標
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react" // 組件語法環境
  },
  "include": [ //編譯目標目錄
    "src",
  ]
}

複製代碼
  • react-app-env.d.ts 是TypeScript聲明文件 🌰例如: 你引入了jQuery 在typescript中:
$('#dom');
// or
jQuery('#dom');
複製代碼

// ERROR: Cannot find name 'jQuery'. 報錯了,由於ts編譯器並不認識$() or jQuery

咱們須要全局定義一下這個變量 用declare修飾符 🌰

declare var jQuery: (selector: string) => any;

jQuery('#foo');
複製代碼

這樣咱們就能夠快樂的使用jquery了

可是前人種樹後人乘涼 不少庫 社區已經給我定義好了 咱們至於要安裝就能夠了

yarn add @types/jquery --save-dev

咱們在後期項目中還會使用到 @types 類型安裝

能夠在 TypeSearch 搜索咱們須要的庫類型

到這裏 咱們已經初步瞭解 Typescript 的項目結構了 正如它官網說的 它是js 的超集,漸進式語言,咱們能夠直接擼,它兼容js目前大部分新語法,而且對其增長類型

安裝 antd, react-router-dom, 。。。

yarn add antd react-router-dom 這裏就不一一例舉🌰了

注意⚠️: react-router-dom 須要安裝@types哦 yarn add @types/react-router-dom --save-dev

antd 工程參考

下面讓個人步入重點~

React-hook

咱們知道 react中分爲2中組件,一種爲類組件,一種爲函數組件

區別

區別 class func
定義方式 class func
狀態 有(state)
this
聲明週期
簡潔度 普通 較爲簡潔

經過比較咱們能夠看到 傳統的函數組件沒有state and 生命週期

因此在以前的開發中 函數式組件一般做爲 一些 簡單的組件 補充

而react-hook 可讓咱們在 函數式組件中使用 state 組件狀態管理,還能夠實現聲明週期 —— 反作用,並且還極大的簡化了代碼,讓代碼更加簡潔明瞭易於理解

userState

咱們看下面2段代碼

import React, { useState } from 'react';

function Example() {
  // 聲明一個叫 "count" 的 state 變量
  const [count, setCount] = useState(0);

  return (
    <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div>
  );
}
複製代碼

等價於

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <div> <p>You clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div>
    );
  }
}
複製代碼
  • 能夠看到咱們導入了 useState 使用數組的解構語法來 定義了2個變量count, setCount 而且傳入了一個初始值 0,並且在下面的使用中咱們擺脫了噁心的this直接可使用
  • conut 是咱們的hook變量 至關於 state
  • setCount 是咱們針對count 的賦值函數 至關於this.setState()

Effect Hook

Effect Hook 可讓你在函數組件中執行反作用操做

看下面的代碼

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // 在組件中更新頁面的title
  useEffect(() => {
    // 設置頁面title
    document.title = `數字 ${count} `;
  });

  return (
    <div> <p>數字 {count}</p> <button onClick={() => setCount(count + 1)}> + 1 </button> </div>
  );
}
複製代碼

等價於class

lass Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  componentDidMount() {
    document.title = `數字 ${this.state.count} `;
  }

  componentDidUpdate() {
    document.title = `數字 ${this.state.count} `;
  }

  render() {
    return (
      <div> <p>數字 {this.state.count} </p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> + 1 </button> </div>
    );
  }
}

複製代碼

咱們能夠看到其中的區別,在class組件中 咱們重複的定義了生命週期 componentDidUpdate,componentDidMount

  • 稍微分析下 咱們使用class的state中的數據是爲了什麼?

    • 咱們知道定義在class 組件中state中的數據咱們會在數據更新後從新渲染dom樹

    • useEffect 在執行 DOM 更新以後調用它。在這個 effect 中,咱們設置了 document 的 title 屬性,不過咱們也能夠執行數據獲取或調用其餘命令式的 API。

    • 注意⚠️:默認狀況下,它在第一次渲染以後和每次更新以後都會執行。(咱們稍後會談到如何控制它。)

    其實useEffect作的很簡單就是告訴 React 組件須要在渲染後執行某些操做,而後每次渲染後都會執行

    下面咱們介紹用useEffect實現聲明週期的 componentDidMountcomponentDidUpdate

    首先在咱們的函數組件中是能夠定義多個useState和useEffect的 他們會依順序執行的

    寫在前面useEffect的返回值決定這個反作用的類型和銷燬

    • return 函數:遇到清除的反作用
    • return [] 空數組:只運行一次的 effect(僅在組件掛載和卸載時執行)
    • 避免死循環

    useEffect === componentDidMount 🌰

    例如咱們要在頁面掛載是時請求數據且渲染到頁面上

    let [list, setList] = useEffect([])
    
    // 獲取數據
    const getList = async () => {
        let res = await axios('xxx')
    }
    
    // 只掛載一次的反作用函數
    useEffect(() => {
        getList()
    }, [])
    
    複製代碼

    useEffect === componentDidUpdate 🌰

    仍是上面那個栗子 咱們須要實時更新mobx中訂閱的store中的數據到頁面上

    let [list, setList] = useEffect([])
     let [data, setData] = useEffect(store.rows)
     
     // 獲取數據
     const getList = async () => {
         let res = await axios('xxx')
     } 
     
     // 獲取store中數據
     const getStore = () => {
         @observer
         let res = store.getxxxxx
         ... 此處省略若干字
         
         setData(res)
     }
     
     // 只掛載一次的反作用函數
     useEffect(() => {
         getList()
     }, [])
     
     // 需清除的反作用函數
     useEffect(() => 
         (()=>
             getStore
         )()
     )
     
    複製代碼

    到這裏咱們已經基本瞭解 reactHook 的基本使用。下面將介紹一個基於ReactHook-useContext 來實現一個簡單的Redux 狀態管理

    Other ReactHook 實現 basic Redux

    基於 前面的 useReducer,和 createContext, useContext

    咱們先來看下 額外hook useReducer:

    const [state, dispatch] = useReducer(reducer, initialArg, init);
    複製代碼

    它接收一個形如 (state, action) => newState 的 reducer,並返回當前的 state 以及與其配套的 dispatch 方法。(是否是有點熟悉,用過redux 的童鞋~)

大概流程圖

import * as React from 'react'
const { useContext, useReducer, createContext } = React

// 根據action.reduce返回狀態
function reducerInAction(state, action) {
    if (typeof action.reducer == 'function') {
        return action.reducer(state)
    }
    return state
}
// 處理store數據
export default function createStore(params) {
    const { _State = {}, reducer
    } = {
        ...params,
        reducer: reducerInAction
    }
    
    // 由createContext 來進行狀態管理數據分發
    const Appcontext = createContext()
    
    const upReducer = (lastState, action) => {
        //更新數據
        let netxState = reducer(lastState, action)
        store._state = netxState
        return netxState
    }
    const store = {
        _state: _State,
        dispatch: undefined,
        getState: () => {
            return store._state
        },
        useContext: () => {
            return useContext(Appcontext)
        }
    }
    // 處理數據後有返回
    const Provider = props => {
        const [state, dispatch] = useReducer(upReducer, _State)
        if (!store.dispatch) {
            store.dispatch = async (action) => {
                if (typeof action === 'function') {
                    await action(dispatch, store.getState())
                } else {
                    dispatch(action)
                }
 
            }
        }
        return <Appcontext.Provider {...props} value={state} /> } return { Provider, store } } 複製代碼

use ⬆️

import * as React from 'react'
import HooksRedux from '@/HooksRedux'
const {
    Provider,
    store
} = _HooksRedux({
    State: { name: '小明', age: '18' }
    
// 請求
const Add_action = () => {
    return {
        type: 'ADD',
        reducer(state) {
            return {
                ...state,
                age: state.age + 1
            }
        }
    }

複製代碼

@ 參考文檔

ps:以上就是basic of redux,可是在個人實際項目中使用的仍是mobx,他的設計模式,和麪向對象的寫法也比較舒服. 可參考


實踐(TSX + Hook)

- 遇事不決 Any —— anyscript

ps: 這固然是不可取的咯~,可是咱們在快速開發過程當中 咱們有時候一個泛型interface 是不肯定的咱們能夠暫時先用any 而後後期更改

  • 小栗子🌰

例如咱們要經過props 向子組件傳遞一個對象,可是這個對象是接口數據,且如今還不能肯定 對象裏面的數據類型和屬性 咱們能夠暫時用 any代替,我通常會在這個地方,打上TODO any Type,方便之後處理。

- React.FC

咱們能夠看到這樣的代碼:

const app: React.FC = () => {
    ...
}
複製代碼

FC = Functional Component SFC = Stateless Functional Component (已棄用)

它是對函數組件的聲明,咱們看一下的Type type React.FC<P = {}> = React.FunctionComponent<P>

在有的時候你不加也沒什麼問題,建議是加上,這樣方便告訴Typescript你的函數組件

- jsDoc and interface

jsDoc 是一個插件 它用於在編輯器|IDE 中給func 添加格式化的註釋, 在Typescript中咱們使用強大的vscode interface是很是重要的,它用於描述個人接口,對象 ...

這裏咱們要注意的是 jsDoc !== interface

  1. 必選屬性 => ":" 帶冒號的屬性是必須存在的,不能夠多也不能少
  2. 可選屬性 => " ? " 表示有選擇的選項,無關緊要
  3. 只讀屬性 => " readonly ":
  4. 任意屬性 [ propName : 類型 ] : any 表示定義了任意屬性取string 類型的值

interface 寫得好 讓咱們的代碼很健壯

- 泛型 and type

例以下面這段代碼

let _userList: Array<object> = []
let [userList, setUserList] = useState(_userList)

let _userList: object[] = []
let [userList, setUserList] = useState(_userList)
複製代碼

使用這2種方式均可以,但並不代編它們 ===

泛型 有着更爲強大的用法

function createArray<T>(length: number, value: T): Array<any> ... function createArray<T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

createArray(3, 'x'); // ['x', 'x', 'x']
複製代碼

- 聯合類型 and 返回值

function getLength(something: string | number): number {
    return something.length;
}
複製代碼

咱們應該嚴格遵循此語法,來進行書寫✍️,沒有返回 void

- 插件

使用 ESlite 來規範咱們書寫的代碼

公司項目Private 不便貼代碼和項目地址

相關文章
相關標籤/搜索