ts + hooks 的基本使用

6,520 阅读3分钟

背景

Hooks 是React 16.8中的新增功能。它们允许您在不编写class的情况下使用状态和其他React功能,关于原理性东西其他大佬已经说很多了,我今天主要讲实践具体用法。

关于recompose --- 高阶组件 hoc 库

作者自己写很推崇 hooks

翻译过来就是

基本api

官方链接传送门
常用api:

  • useState
  • useEffect
  • useReducer
  • useRef
  • useContext

常用 api

useState

     const [storeCustomerBaseInfo, updateStoreCustomerBaseInfo] = useState({})

数组第一个值是你需要更新的值,第二个值是更新该值得函数 useState() 函数运行的时候给默认值 我这里是给的空对象 具体更新栗子如下

if (res.success) {
      const { storeCustomerBaseInfo, storeUserPayInfo } = res.data
      updateStoreCustomerBaseInfo(storeCustomerBaseInfo)
    }

useEffect

通俗点就是 componentDidMount,componentDidUpdate、componentWillUnmount三个生命周期的合集,

一般在这里发起异步请求🌰

useEffect(() => {
    getWebData(id)
  }, [id])
  const getWebData = async (id) => {
    const res = await CustomerService.getCustomerDeatil(id)
    if (res.success) {
      const { storeCustomerBaseInfo, storeUserPayInfo } = res.data
      updateStoreCustomerBaseInfo(storeCustomerBaseInfo)
    }
  }

细心的同学会发现我在函数末尾多加了个参数 这里相当于我类似于 didMount 方法,如果不加该参数,就会一直请求。 默认情况下,它在第一次渲染之后和每次更新之后运行,你可能会发现更容易认为效果发生在“渲染之后”,而不是考虑“挂载”和“更新”React保证DOM在运行‘效果’时已更新。 加参数就相当于自定义更新,比如我这里加了 id 只有当id 重新变化的时候再执行该方法

如何做一些取消操作呢?

比如定时器,发布订阅,有时候组件卸载的时候要取消这个时候该怎么办呢

解决办法

return 一个新的函数

 useEffect(() => {
     fecth()
 return () => {
     clearTimeOut()
 }
})

useReducer

useState的替代方案。接受类型为(state,action) => newState的reducer,并返回与dispatch方法配对的当前状态。 (如果熟悉Redux,你已经知道它是如何工作的。) 用过 redux的相信对这个reducer 都不陌生 使用和redux 如出一撤

//actions.js
export const showMsg = 'SHOW_MSG'
export const openpoolInfo = 'OPENPOOL_INFO'
export const updateShowMsg = (data) => ({
type: showMsg,
data
})
export const updateOpenpoolInfo = (data) => ({
type: openpoolInfo,
data
})

  • reducer 部分
import {
showMsg,
openpoolInfo
} from './actions'
export const initState = {
showMsg: true,
openpoolInfo: {}
}
export const initReducer = (state, action) => {
switch (action.type) {
  case showMsg:
    return {
      ...state,
      showMsg: action.data
    }
  case openpoolInfo:
    return {
      ...state,
      openpoolInfo: action.data
    }
  default:
    return state
}
}

最后链接组件

import React, { useEffect, useReducer } from 'react'
import { Page } from '../../components'
import { Divider, Button, message } from 'antd'
import { CustomerService } from '../../service'
import BaseInfo from './base_info'
import Edit from './edit'
import { yuan2Fen, fen2Yuan } from '../../helper'
import { updateShowMsg, updateEditTotalOpenPoolAmount, updateOpenpoolInfo } from './store/actions'
import { initState, initReducer } from './store/reducer'
const OpenPool = (props) => {
  const [state, dispatch] = useReducer(initReducer, initState)
  const { params: {id} } = props.match
  useEffect(() => {
    getOpenpoolInfo(id)
  }, [id])
  const getOpenpoolInfo = async (id) => {
    const res = await CustomerService.getOpenpool(id)
    if (res.success) {
      dispatch(updateOpenpoolInfo(res.data))
    }
  }
  const editChange = (editTotalOpenPoolAmount) => {
    const { usedOpenPollAmount } = state.openpoolInfo
    const showMsg = fen2Yuan(usedOpenPollAmount) - editTotalOpenPoolAmount > 0
    dispatch(updateShowMsg(showMsg))
    dispatch(updateEditTotalOpenPoolAmount(editTotalOpenPoolAmount))
  }
  const getParms = () => {
    const { usedOpenPollAmount } = state.openpoolInfo
    return {
      customerId: id,
      usedOpenPollAmount,
      totalOpenPoolAmount: yuan2Fen(state.editTotalOpenPoolAmount)
    }
  }
  const updateAmount = async () => {
    const params = getParms()
    const res = await CustomerService.updateOpenpool(params)
    if (res.data) {
      message.success('操作成功')
    }
  }
  return (
    <Page title='xxx'>
      <BaseInfo {...state.openpoolInfo} />
      <Divider />
      <Edit
        showMsg={state.showMsg}
        {...state.openpoolInfo}
        editTotalOpenPoolAmount={state.editTotalOpenPoolAmount}
        editChange={editChange}
      />
    </Page>
  )
}
export default OpenPool

TS 的用法

其实和上面的没太大变化只要设置类型就行了,当然你也可以不设置

import { Button } from 'antd'
import * as React from "react";
const { useState } = React
const Home = () => {
 const [count, setCount] = useState(0)
 const [strings, setStrings] = useState<string[]>([])
 return (<div>
   <Button onClick={() => setCount(1)}>测试普通类型</Button>
   <Button onClick={() => setStrings([])}>测试检测类型</Button>
 </div>)
}
export default Home

再来看看使用 interface

import { Button } from 'antd'
import * as React from "react";
const { useState } = React
interface IType {
 counts: number;
}

const About = () => {
 const [counts, setCount] = useState<IType[]>([]);
 return (<div>
   <Button onClick={() => setCount([{counts: 1}])}>测试普通类型</Button>
   <Button onClick={() => setCount([]}>测试普通类型</Button>
 </div>)
}
export default About

注意在某些 tslint 版本里面 定义interface 一定要用 Ixxx 开头不然会一直爆红 具体请看issue

彩蛋

以上是我个人的实践,现在估计看完都会有疑问到底是用 useState 还是 useReducer, 看了国外某大神的文章你就明白了 传送门