目錄
1 爲什麼使用 React Hooks
對於一個新玩意(這也不算新玩意了)的使用肯定是有他的原因的撒,我們先來個簡單例子看看,現在我們做過超級超級簡單的例子:點擊按鈕數字增加1
先用我們原來的做法(用class的方式定義的組件)
import React, { Component } from 'react'
class Increased extends Component {
constructor (props) {
super (props)
this.state={count:0}
}
render() {
return (
<div>
<p>總數:{this.state.count}</p>
<button onClick={this.add.bind(this)}>增加</button>
</div>
)
}
add() {
this.setState({count:this.state.count+1})
}
}
export default Increased
咋們在來看看使用React Hooks做的
import React,{useState } from 'react'
const IncreasedHooks = () => {
const [ count , setCount ] =useState(0)//數組解構
return (
<div>
<p>總數:{count}</p>
<button onClick={()=>setCount(count+1)}>增加</button>
</div>
)
}
export default IncreasedHooks
下面是效果:
2 詳解 useState
useState
是react自帶的一個hook函數,它的作用是用來聲明狀態變量。useState這個函數接收的參數是狀態的初始值
,它返回一個數組
,這個數組的第0位是當前的狀態值
,第1位是可以改變狀態值的方法函數
- 如何聲明 根據上面的代碼就知道啦
const [ count , setCount ] =useState(0)//數組解構
- 如何讀取
因爲返回的是個數組,按照取數組的元素的方式取就行了啦,讀取是很簡單的。只要使用{count}就可以,因爲這時候的count就是JS裏的一個變量,想在JSX中使用,值用加上{}就可以。
建議使用解構的方式,簡單快速,什麼? 你不會解構? 那你戳這個吧解構賦值這些些你必須知道
<p>總數:{count}</p>
- 使用 改變state 我們就要使用返回的數組中的第二個值啦
<button onClick={()=>setCount(count+1)}>增加</button>
3 useEffect的使用
3.1useEffect代替常用的生命週期函數
useEffect
可以用來代替我們常用的聲明周期函數 ,那我們一般什麼時候使用生命週期函數呢,當然是我們在是做"副作用"的業務處理 代替了componentDidMount
和componentDidUpdate
。分別在組件第一次渲染後在瀏覽器控制檯打印出計數器結果和在每次計數器狀態發生變化後打印出結
使用useEffect時候有兩點需要注意的
-
React首次渲染和之後的每次渲染都會調用一遍useEffect函數,而之前我們要用兩個生命週期函數分別表示首次渲染(componentDidMonut)和更新導致的重新渲染(componentDidUpdate)。
-
useEffect中定義的函數的執行不會阻礙瀏覽器更新視圖,也就是說這些函數時異步執行的,而
componentDidMonut
和componentDidUpdate
中的代碼都是同步執行的。個人認爲這個有好處也有壞處吧,比如我們要根據頁面的大小,然後繪製當前彈出窗口的大小,如果時異步的就不好操作了。
const IncreasedHooks = () => {
const [ count , setCount ] =useState(0)
useEffect(()=>{
console.log(`useEffect=>You clicked ${count} times`)
})
//解決生命週期函數 代替了componentDidMount和componentDidUpdate。分別在組件第一次渲染後在瀏覽器控制檯打印出計數器結果和在每次計數器狀態發生變化後打印出結
return (
<div>
<div>使用React Hooks</div>
<p>總數:{count}</p>
<button onClick={()=>setCount(count+1)}>增加</button>
</div>
)
}
3.2 實現類似componentWillUnmount(組件將要被卸載時執行)
使用路由實現組件的解綁,需要用到useEffect
函數裏面返回一個函數的形式,代替解綁生命週期函數 componentWillUnmount 組件將要被卸載時執行
const Index = () => {
useEffect(()=>{
console.log('useEffect=>老弟你來了!Index頁面')
return ()=>{
console.log('老弟,你走了!Index頁面')
}//返回一個函數的形式,代替解綁生命週期函數 componentWillUnmount 組件將要被卸載時執行
},[])
return <div>加油,程序員</div>
}
const List = () =>{
return (
<ul>
<li>你好</li>
<li>我好</li>
<li>他好</li>
</ul>
)
}
const IncreasedHooks = () => {
return (
<div>
<Router>
<ul>
<li><Link to = "/">首頁 </Link></li>
<li><Link to = "/list/">列表頁 </Link></li>
</ul>
<Route path ="/" exact component={Index}></Route>
<Route path ="/list/" component={List}></Route>
</Router>
</div>
)
}
其實這個主要是使用的useEffect
的第二個參數,上面的程序中,不是用第二個參數的時候.每次狀態發生變化,useEffect
都進行了解綁。真正實現主要是第二個人函數加了空數組.useEffect的第二個參數,它是一個數組,數組中可以寫入很多狀態對應的變量,意思是當狀態值發生變化時,我們才進行解綁。但是當傳空數組[]時,就是當組件將被銷燬時才進行解綁,這也就實現了componentWillUnmount的生命週期函數。
我的理解是:第二個參數是實現解綁條件
例如:給計數器也加上解綁:只需要在返回的數組中寫入記錄計數的狀態值count 變量
const IncreasedHooks = () => {
const [ count , setCount ] =useState(0)//數組解構
useEffect(()=>{
console.log(`useEffect=>You clicked ${count} times`)
return ()=>{
console.log('====================')
}
},[count])
return (
<div>
<p>總數:{count}</p>
<button onClick={()=>setCount(count+1)}>增加</button>
</div>
)
}
4 useContext的使用
useContext
主要是用來實現父子組件之間的傳值 如下代碼實現
import React,{useState ,useContext, createContext } from 'react'
const CountContext = createContext()
// 定義子組件
const Coounter = () =>{
//子組件一句話就可以得到父組件傳遞過來的count
const count = useContext(CountContext)
return (<h2>{count}</h2>)
}
// 父組件
const IncreasedHooks2= () => {
const [ count , setCount ] =useState(0)
return (
<div>
<div>使用React Hooks</div>
<p>總數:{count}</p>
<button onClick={()=>setCount(count+1)}>增加</button>
{/* 父組件向組件提供值 */}
<CountContext.Provider value={count} >
<Coounter/>
</CountContext.Provider>
</div>
)
}
export default IncreasedHooks2
5 useReducer的使用
5.1 useReducer時實現reducer
useContext
和useReducer
合作可以完成類似的Redux庫的操作,useReducer
可以讓代碼具有更好的可讀性和可維護性,它類似於Redux中的reducer
,reducer
這個函數接收兩個參數,一個是狀態,一個用來控制業務邏輯的判斷參數
一個簡單reducer的例子來理解什麼是reducer
function countReducer(state, action) {
switch(action.type) {
case 'add':
return state + 1;
case 'sub':
return state - 1;
default:
return state;
}
}
使用useReducer
import React, { useReducer } from 'react';
const IncreasedHooks2 = () => {
const [count, dispatch] = useReducer((state, action) => {
switch (action) {
case 'add':
return state + 1
case 'sub':
return state - 1
default:
return state
}
}, 0)
return (
<div>
<h2>現在的分數是{count}</h2>
<button onClick={() => dispatch('add')}>Increment</button>
<button onClick={() => dispatch('sub')}>Decrement</button>
</div>
)
}
export default IncreasedHooks2
5.2 useReducer useContext實現redux的狀態管理和狀態共享
實現狀態全局化並能統一管理,統一個事件的派發
案例:點擊按鈕切換對應的字體顏色
//父組件
import React from 'react';
import Buttons from './Buttons';
import ShowArea from './ShowArea'
import { Color } from './Color'; //引入Color組件
const ChangeColor = () => {
return (
<div>
<Color>
<Buttons />
<ShowArea />
</Color>
</div>
)
}
export default ChangeColor
//字體展示組件
import React,{useContext} from 'react'
import { ColorContext } from './Color';
const ShowArea = () => {
// 獲取color
const {color} = useContext(ColorContext)
return (
<div>
<div style={{color:color}}>字體顏色爲{color}</div>
</div>
)
}
export default ShowArea
//按鈕組件
import React ,{useContext} from 'react';
import {ColorContext,UPDATE_COLOR} from './Color'
const Buttons = () => {
// 獲取共享的dispatch
const {dispatch} = useContext(ColorContext)
return (
<div>
{/* 使用dispatch派發一個action */}
<button onClick= {()=> {dispatch({type:UPDATE_COLOR,color:"red"})}}>紅色</button>
<button onClick= {()=> {dispatch({type:UPDATE_COLOR,color:"yellow"})}}>黃色</button>
</div>
)
}
export default Buttons
//狀態管理
import React,{createContext ,useReducer } from 'react'
export const ColorContext = createContext()
export const UPDATE_COLOR = "UPDATE_COLOR"
// 定義reducer
const reducer = (state, action) => {
switch (action.type) {
case UPDATE_COLOR:
return action.color
default:
return state
}
}
// 顏色共享
export const Color = props => {
// 使用reducer
const [color, dispatch] = useReducer(reducer,'red')
return (
<div>
{/* 將color和dispatch共享出去 */}
<ColorContext.Provider value={{color,dispatch}}>
{props.children}
</ColorContext.Provider>
</div>
);
}
結果
6. useMemo
useMemo
主要用來解決使用React hooks產生的無用渲染的性能問題,函數型組件沒有shouldCompnentUpdate
(組件更新前觸發),我們就沒有辦法通過組件前的條件來決定組件是否更新.
且在函數組件中,也不再區分mount
和update
兩個狀態,這意味着函數組件的每一次調用都會執行內部的所有邏輯,就帶來了非常大的性能損耗。useMemo
和useCallback
都是解決上述性能問題的
import React , {useState,useMemo} from 'react';
function ComeHere(){
const [he, setHe] = useState('他在等着')
const [me, setMe] = useState('我在等着')
return (
<>
<button onClick={()=>{setHe(new Date().getTime())}}>他</button>
<button onClick={()=>{setMe(new Date().getTime()+',我走來了')}}>我</button>
<ChildComponent name={he}>{me}</ChildComponent>
</>
)
}
function ChildComponent({name,children}){
function changeHe(name){
console.log('她來了,她來了。他向我們走來了')
return name+',他向我們走來了'
}
//爲了解決當我們點擊"我"按鈕時,"他"對應的changeHe方法不能執行,只有在點擊他按鈕時才能執行。才能減少子組件的多次沒有用的重新渲染
//其實只要使用useMemo,然後給她傳遞第二個參數,參數匹配成功,纔會執行。
const actionHe = useMemo(()=>changeHe(name),[name])
return (
<>
<div>{actionHe }</div>
<div>{children}</div>
</>
)
}
7. useRef
-
用
useRef
獲取React JSX中的DOM元素,獲取後你就可以控制DOM的任何東西了。但是一般不建議這樣來作,React界面的變化可以通過狀態來控制。 -
用
useRef
來保存變量,這個在工作中也很少能用到,我們有了useContext這樣的保存其實意義不大
import React, { useRef} from 'react';
function Example(){
//聲明一個input的element
const inputEl = useRef(null)
const onButtonClick=()=>{
inputEl.current.value="Hello ,JSPang"
console.log(inputEl) //輸出獲取到的DOM節點
}
return (
<>
{/*保存input的ref到inputEl */}
<input ref={inputEl} type="text"/>
<button onClick = {onButtonClick}>在input上展示文字</button>
</>
)
}
export default Example
8. 自定義Hooks函數
實例,自第一個實時監測瀏覽器窗口大小的Hooks函數
自定義Hooks函數,記住一定要用use開頭
import React,{ useState ,useEffect ,useCallback } from 'react';
const useWinSize = () =>{
const [size,setSize] = useState({
width:document.documentElement.clientWidth,
height:document.documentElement.clientHeight
})
//useCallback,目的是爲了緩存方法(useMemo是爲了緩存變量)
const onResize = useCallback(() => {
setSize({
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
})
},[])
useEffect(()=>{
window.addEventListener('resize',onResize)
return ()=>{
window.removeEventListener('resize',onResize)
}
},[])
return size
}
//組件中使用
const MyHooks = ()=>{
const size = useWinSize()
return <div>size:{size.width}x{size.height}</div>
}
export default MyHooks