用了一年多的React.js 但是在公司一直用的是Class 组件,但是被问到React 提高性能有什么手段的时候,我却哑口无言,不能说出一个 一二三来,连React官方推荐的Hook也是没有用过。学了之后hook 之后,感觉和Class 组件对比来说,在很多场景用hook,函数组件是比较方便的,一些简单的页面如果用Class 组件,显得有些 “臃肿”。
优化可以用useCallback ,useMemo和memo
两者是编程思想的改变:Class组件是面向对象,封装的编程实现而 函数组件开发,是一种链式编程。并且Hook使函数组件有了状态(state)
import React, { useState } from 'react'
function UseStateExam() {
const [count, setCount] = useState(0)
return (
<div>
<p>You clicked : {count}</p>
<Button onClick={() => setCount(count + 1)}>count ++</Button>
</div>
)
}
useState 不能放在条件判断和循环中(在子函数中调用呢?)
useEffect 是异步的,不会阻断视图的更新,所有不能实时的计算页面的大小。
useEffect 接受两个参数
第一个参数是执行的函数,可以返回一个函数。
第二个参数是一个数组,可以不传。
当第二个参数没有传的时候,页面第一次渲染和useState 变化时,都会触发函数执行,相当于componentDidMount和 componentDidUpdate组合起来的功能。即当useEffect没有第二个参数时,组件的初始化和更新都会执行。
function UseEffectExam() {
const [count, setCount] = useState(0)
useEffect(() => {
console.log(`useEffect 执行 ${count}`)
})
return (
<div>
<p>You clicked : {count}</p>
<Button onClick={() => setCount(count + 1)}>count ++</Button>
</div>
)
}
当第二个参数是一个空数组时,页面第一次渲染时函数执行,相当于componentDidMount
function UseEffectExam() {
const [count, setCount] = useState(0)
useEffect(() => {
console.log(`useEffect 执行 ${count}`)
}, [])
return (
<div>
<p>You clicked : {count}</p>
<Button onClick={() => setCount(count + 1)}>count ++</Button>
</div>
)
}
当第二个参数是一个包含 UseState 的数据,当数组的里的state 发生变化时,函数执行
function UseEffectExam() {
const [count, setCount] = useState(0)
useEffect(() => {
console.log(`useEffect 执行 ${count}`)
}, [count])
return (
<div>
<p>You clicked : {count}</p>
<Button onClick={() => setCount(count + 1)}>count ++</Button>
</div>
)
}
实现 componentDidMount 和 componentWillUnmount,数组为空时,且第一个参数返回个函数,那么页面挂载和卸载的时候触发这个函数
function UseEffectExam() {
const [count, setCount] = useState(0)
useEffect(() => {
console.log(`useEffect 执行 ${count}`)
return () => {
console.log("页面卸载");
}
}, [count])
return (
<div>
<p>You clicked : {count}</p>
<Button onClick={() => setCount(count + 1)}>count ++</Button>
</div>
)
}
初始化的时候不执行 return 的方法,count变化事执行(如果是空数据不执行),页面卸载是时候执行
使用createContext实例化一个useContext对象: CountContext。
CountContext.Provider 包裹着的子组件,都可以用 let count = useContext(CountContext)得到父组件的count值
import React, {,useState,useContext,createContext} from 'react'
const CountContext = createContext()
function UseContextExam() {
const [count, setCount] = useState(0)
return (
<div>
<h2>父组件</h2>
<p>You clicked : {count}</p>
<Button onClick={() => setCount(count + 1)}>count ++</Button>
<CountContext.Provider value={count}>
<Childer1 />
</CountContext.Provider>
</div>
)
}
function Childer1() {
// CountContext 这个实例是要和父组件的是同一个实例才可以?
let count = useContext(CountContext)
return (
<div>
<h2>子组件1</h2>
<p>子组件接受到:{count}</p>
</div>
)
}
useReducer接受一个函数和默认值
function UseReducerExam() {
const [count, dispatch] = useReducer((state, action) => {
switch (action) {
case "add":
return state + 1
case "sub":
return state - 1
default:
return state
}
}, 0)
return (
<div>
<p>You clicked : {count}</p>
<Button onClick={() => dispatch('add')}>count ++</Button>
<Button onClick={() => dispatch('sub')}>count --</Button>
</div>
)
}
import React, { createContext, useReducer } from 'react'
const ColorContext = createContext({})
const UpColor = 'UpColor'
const reducer = (state, action) => {
switch (action.type) {
case UpColor:
return action.color
default:
return state
}
}
const Color = props => {
const [color, dispatch] = useReducer(reducer, 'green')
return (
<ColorContext.Provider value={{ color, dispatch }}>
{props.children}
</ColorContext.Provider>
)
}
export {
Color,
ColorContext,
UpColor,
}
import { Button, } from "antd";
import { useContext } from 'react'
import { ColorContext, UpColor, } from './Color'
function Buttons() {
const { dispatch } = useContext(ColorContext)
return (
<div>
<Button onClick={() => dispatch({ type: UpColor, color: "red" })}>红色</Button>
<Button onClick={() => dispatch({ type: UpColor, color: "yellow" })}>黄色</Button>
</div>
)
}
export default Buttons
import React, { useContext } from 'react'
import { ColorContext, } from './Color'
function ShowArea() {
const { color } = useContext(ColorContext)
return (
<div style={{ color }}>
字体颜色为red
</div>
)
}
export default ShowArea
import Buttons from "./ButtonView";
import ShowArea from "./useReducerDome";
import { Color } from './Color'
function UseReducerTest() {
return (
<div>
<Color>
<Buttons />
<ShowArea />
</Color>
</div>
)
}
export default UseReducerTest
为什么我的usememo 子组件在父组件更改商品名称的时候,也会重复渲染呢
function UseMomeExam() {
const [itemName, setItemName] = useState("商品" + 1)
const [itemPrice, setItemPrice] = useState(0)
const [itemCount, setItemCount] = useState(0)
const MemeChildren = useMemo(() => UseMomeChildren, [itemPrice,setItemCount])
return (
<div>
<span>商品名称:{itemName}</span>
<span>商品单价:{itemPrice}</span>
<span>商品数量:{itemCount}</span>
<br />
<Button onClick={() => setItemPrice(itemPrice + 1)}>单价++</Button>
<Button onClick={() => setItemCount(itemCount + 1)}>数量++</Button>
<Button onClick={() => setItemName(itemName + 1)}>更换商品</Button>
<MemeChildren itemPrice={itemPrice} itemCount={itemCount} />
</div>
)
}
function UseMomeChildren(props) {
const { itemCount, itemPrice } = props
console.log("子组件渲染", props);
return (
<div>
<h3>商品总价:{itemCount * itemPrice}</h3>
</div>
)
}
useRef 有两个作用
function UseRefExam() {
const inputEl = useRef(null)
// 获取DOM input
const onButtonClick = () => {
// inputEl.current.value = "Hello World"
console.log("inputEl", inputEl);
}
const [text, setText] = useState('Jspang')
const textRef = useRef()
// setText触发,就将输入框的内容保存
useEffect(() => {
textRef.current = text
console.log('textRef.current', textRef.current);
})
return (
<div>
<Input ref={inputEl} type="text" />
<Button onClick={onButtonClick}>在input上的文字</Button>
<br />
<br />
<Input value={text} onChange={(e) => setText(e.target.value)} />
</div>
)
}
useCallback缓存方法,第一参数是一个函数,第二个是一个数据,空数组表示只执行一次
监听页面大写
自定义一个useWinSize的hook,其实就是封装一个方法/组件,
import React, { useState,useEffect,useCallback,} from 'react'
import { Button, Input } from 'antd'
function useWinSize() {
const [size, setSize] = useState({
width: document.documentElement.clientWidth,
height: document.documentElement.height
})
const onResize = useCallback(
() => {
setSize({
width: document.documentElement.clientWidth,
height: document.documentElement.height
})
},
[],
)
useEffect(() => {
window.addEventListener('resize', onResize)
return () => {
window.removeEventListener('resize', onResize)
}
})
return size
}
export default useWinSize
使用自定义hook
import useWinSize from './useWinSize'
function HookTesh() {
const size = useWinSize()
return (
<div>页面的Siz:{size.width} * {size.height}</div>
)
}
学习地址bilibili 技术胖:https://www.bilibili.com/video/BV1y4411Q7yH?p=11