從實際開發的角度去看react的新特性hooks

React v16.7.0-alpha中加入的新特性Hooks。它可讓你在函數式組件中使用state和生命週期。本人花了點時間研究了下,發現網上大部分的教程都是偏向於理論,或者乾脆翻譯官網和代碼,缺乏實際使用場景的教程,因此本人根據本身的理解寫了這篇從開發角度去看這個hooks新特性(高手勿拍)react

前言

理論方面的我不會講太多,你們能夠搜一搜,很是多的文章。或者直接去看官網。我主要會講我在項目開發中會怎麼使用它。ios

hooks是幹什麼的

簡單的來講,就是讓你的函數式無狀態組件,支持使用狀態組件的state和生命週期。另外能夠解決this綁定這個麻煩的東西。(代碼量也會減小不少)git

經常使用的都有什麼

相對我開發中經常使用的hooks大體有useState、useReducer、useEffect。另外還有一些我自定義的hooks。github

直接開始用hooks

接下來咱們直接開始用,跳過使人頭疼的理論,直接在使用中理解他們究竟是幹什麼的。redux

首先咱們須要一個基於react的開發框架,這裏打個小廣告,推薦本身前段時間分享的一篇文章《如何搭建一個REACT全家桶框架》axios

在原先的userInfo頁面裏面進行一個常規查詢列表功能的編寫。不用hooks功能的寫法大體以下: (後面的例子都是基於antd,如何添加支持和按需加載,請看官網文檔或者我上傳的代碼配置,就不細說與hooks無關的東西了)api

import React, {PureComponent} from 'react';
import {connect} from 'react-redux';
import {getUserInfo} from "actions/userInfo";
import { Input, Button, List } from 'antd';
import style from './index.less'

// class組件
class UserInfo extends PureComponent {
    constructor(props) {
        super(props);
        // state初始化
        this.state = {
            title: ""
        };
    }

    componentDidMount(){
    // 解構state
        const { title } = this.state;
        this.doSearch(title);
    }

    doSearch = (title) => {
        this.props.getUserInfo(title);
    }

    handleChange=(e) => { 
        // 更新state
        this.setState({
            title:e
        })
    }

    render() {
        const { list=[] } = this.props.userInfo;
        return (
            <div className={style.userInfo}>
                <p>
                    標題:<Input style={{width:'100px'}} onChange={this.doSearch} />
                    <Button  type="primary" onClick={this.doSearch}>查詢</Button>
                </p>
                <List
                    header={<div>列表</div>}
                    footer={null}
                    bordered
                    dataSource={list}
                    renderItem={item => (<List.Item>{item}</List.Item>)}
                />
            </div>
        )
    }
}

export default connect((userInfo) => userInfo, {getUserInfo})(UserInfo);
複製代碼

從上面代碼中,咱們看到了state三個相關的地方,初始化---更新---解構獲取,接下來咱們使用hooks來寫。bash

import React, { useState, useEffect } from 'react';
import {connect} from 'react-redux';
import {getUserInfo} from "actions/userInfo";
import { Input, Button, List } from 'antd';
import style from './index.less'

// 函數式組件
const UserInfo = (props) => {
    
    // 初始化state
    const [title, setTitle] = useState("");
    const [search, setSearch] = useState(false);

    // 查詢數據
    useEffect(() => {
        props.getUserInfo(title);
    }, [search]);


    // 參數變化
    const handleChange=(e) => { 
        setTitle(e.target.value);
    }

    // 執行查詢
    const doSearch = () => {
        setSearch(!search);
    }

    const { list=[] } = props.userInfo;
    return (
        <div className={style.userInfo}>
            <p>
                標題:<Input style={{width:'100px'}} onChange={(e) => { handleChange(e) }} />
                <Button  type="primary" onClick={() => { doSearch() }}>查詢</Button>
            </p>
            <List
                header={<div>列表</div>}
                footer={null}
                bordered
                dataSource={list}
                renderItem={item => (<List.Item>{item}</List.Item>)}
            />
        </div>
    )
    
}

export default connect((userInfo) => userInfo, {getUserInfo})(UserInfo);
複製代碼

讓咱們對比一下,都有哪些不一樣的地方antd

  • 統一組件,全部組件都是無狀態組件(Funciont)
  • 再也不須要綁定this
  • 沒有構造函數,內部狀態使用useState
  • 沒有複雜的生命週期,生命週期使用useEffect(注意這裏的參數,它改變就會執行useEffect)
  • 改變state使用它自身提供的setSearch方法

根據上面對比的結果,咱們發現,之後寫組件只須要寫無狀態組件了。而且不須要煩惱this。生命週期也不須要寫那麼多,一個useEffect就夠了。而且你靈活控制它的第二個參數,能夠決定何時去執行它。是否是方便不少。數據結構

useReducer對比useState

官網對於內部狀態管理,提供了兩個方法,另一個就是useReducer。

若是你熟悉redux的話,你會以爲它很眼熟。咱們先寫一個,用userReducer來替代useState,而後在來講說爲何用它!

import React, { useReducer, useEffect } from 'react';
import {connect} from 'react-redux';
import {getUserInfo} from "actions/userInfo";
import { Input, Button, List } from 'antd';
import style from './index.less'



function reducer(state, action) {
  switch (action.type) {
    case 'change':
      return {
          ...state,
          ...action.playod
      };
  }
}

const UserInfo = (props) => {
    
    // 初始化reducer
    const initialState = {title: "", search:false};
    const [state, dispatch] = useReducer(reducer, initialState);
 
    // 查詢數據
    useEffect(() => {
        props.getUserInfo(state.title);
    }, [state.search]);


    // 參數變化
    const handleChange=(e) => { 
        dispatch({type: 'change', playod:{title:e.target.value}})
    }

    // 執行查詢
    const doSearch = () => {
        dispatch({type: 'change', playod:{search:!state.search}})
    }

    const { list=[] } = props.userInfo;
    return (
        <div className={style.userInfo}>
            <p>
                標題:<Input style={{width:'100px'}} onChange={(e) => { handleChange(e) }} />
                <Button  type="primary" onClick={() => { doSearch() }}>查詢</Button>
            </p>
            <List
                header={<div>列表</div>}
                footer={null}
                bordered
                dataSource={list}
                renderItem={item => (<List.Item>{item}</List.Item>)}
            />
        </div>
    )
    
}

export default connect((userInfo) => userInfo, {getUserInfo})(UserInfo);
複製代碼

用法上和redux類似,經過純函數和action來執行改變。兩個對比之下差距不大,可是根據官方推薦和一些大佬的見解,推薦使用useReducer。

  • 統一管理全部的狀態數據
  • 能夠處理比較複雜的數據結構

可是我的以爲和redux同時使用的話,對數據存放位置,和兩個dispatch的使用比較不友好。各位使用者根據本身的喜愛選擇吧。

自定義hooks

除了官方提供的hooks,咱們還能夠自定義一些hooks,這裏我就根據個人需求定義一個請求數據的hooks。方便你們理解使用。

src/hooks/request.js

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

const useSearchData = (url, params) => {
     // 是否正在請求中
     const [isLoading, setIsLoanding] = useState(false);
     // 請求參數
     const [queryParams, setQueryParams] = useState(params);
     // 請求結果
     const [data, setData] = useState([]);

    // 查詢數據
    const queryTableData = (params) => {
        setIsLoanding(true);
        axios.post(url).then((res)=>{
            let data = JSON.parse(res.request.responseText);
            const list = data.list.filter((item, index) => {
                return item.indexOf(params) > -1
            })
            setIsLoanding(false);
            setData(list);
        })
    }

    // 根據參數變化決定是否請求數據
    useEffect(() => {
        queryTableData(queryParams);
    }, [queryParams]);

     // 供外部調用
     const doRequest = (params) => {
        setQueryParams(params);
    }

    return {
        isLoading,
        data,
        doRequest
    };
} 

export default useSearchData;
複製代碼

這裏咱們寫了一個自定義hooks--useSearchData(注意,自定義的hooks要以use開頭,切記!!!)。它提供了咱們須要的三個結果

  • isLoading 是否在加載
  • data 請求到的結果集
  • doRequest 更換參數再一次請求
src/userInfo/index.js

import React, { useReducer, useEffect } from 'react';
import {connect} from 'react-redux';
import {getUserInfo} from "actions/userInfo";
import { Input, Button, List } from 'antd';
import useSearchData from 'hooks/request'; 
import style from './index.less';



function reducer(state, action) {
  switch (action.type) {
    case 'change':
      return {
          ...state,
          ...action.playod
      };
  }
}

const UserInfo = (props) => {
    
    // 初始化reducer
    const initialState = {title: "", search:false};
    const [state, dispatch] = useReducer(reducer, initialState);
    const {isLoading, data, doRequest} = useSearchData('/api/getList',state.title);
    // 查詢數據
    useEffect(() => {
        props.getUserInfo(data);
    }, [data]);


    // 參數變化
    const handleChange=(e) => { 
        dispatch({type: 'change', playod:{title:e.target.value}})
    }

    // 執行查詢
    const doSearch = () => {
        doRequest(state.title);
    }

    const { list=[] } = props.userInfo;
    return (
        <div className={style.userInfo}>
            <p>
                標題:<Input style={{width:'100px'}} onChange={(e) => { handleChange(e) }} />
                <Button  type="primary" onClick={() => { doSearch() }}>查詢</Button>
            </p>
            <List
                header={<div>列表</div>}
                footer={null}
                loading={isLoading}
                bordered
                dataSource={list}
                renderItem={item => (<List.Item>{item}</List.Item>)}
            />
        </div>
    )
    
}

export default connect((userInfo) => userInfo, {getUserInfo})(UserInfo);
複製代碼

在咱們的UsesInfo組件中,咱們首先調用useSearchData獲取isLoading, data, doRequest。在useEffect鉤子中根據data來判斷是否要更新數據到redux。最後當咱們更改查詢參數須要更新list的時候,執行doRequest便可。

這樣咱們的自定義查詢hooks就寫好了,其實就是把公共的東西抽取出來,封裝成一個hooks。尤爲是之前的高階組件和渲染組件,層級嵌套過深,很差維護。寫成hooks的形式,都是函數式組件,無嵌套層級。

結尾和GIT地址

本人在項目中經常使用的hooks就介紹完了,有些人可能以爲加上了useContext是否是徹底能夠替代redux?我的理解仍是不同,全局的狀態管理,最好仍是依賴於redux和mobx作管理較好。請各位小夥伴自行體驗!!! GIT地址

相關文章
相關標籤/搜索