初识React Hooks,函数式编程

React Hook 技术胖

用了一年多的React.js 但是在公司一直用的是Class 组件,但是被问到React 提高性能有什么手段的时候,我却哑口无言,不能说出一个 一二三来,连React官方推荐的Hook也是没有用过。学了之后hook 之后,感觉和Class 组件对比来说,在很多场景用hook,函数组件是比较方便的,一些简单的页面如果用Class 组件,显得有些 “臃肿”。
优化可以用useCallback ,useMemo和memo

Class 组件和函数组件式

两者是编程思想的改变:Class组件是面向对象,封装的编程实现而 函数组件开发,是一种链式编程。并且Hook使函数组件有了状态(state)

useState
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 是异步的,不会阻断视图的更新,所有不能实时的计算页面的大小。

useEffect 接受两个参数

第一个参数是执行的函数,可以返回一个函数。

第二个参数是一个数组,可以不传。

  1. 当第二个参数没有传的时候,页面第一次渲染和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>
      )
    }
    
  2. 当第二个参数是一个空数组时,页面第一次渲染时函数执行,相当于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>
      )
    }
    
  3. 当第二个参数是一个包含 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>
      )
    }
    
  4. 实现 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变化事执行(如果是空数据不执行),页面卸载是时候执行

useContext和createContext

使用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

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>
  )
}
UseReducer 和 useContext 结合案例
Color.js
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,
}
ButtonView.js
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
ShouArea
import React, { useContext } from 'react'
import { ColorContext, } from './Color'

function ShowArea() {
  const { color } = useContext(ColorContext)
  return (
    <div style={{ color }}>
      字体颜色为red
    </div>
  )
}

export default ShowArea
index.js
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

为什么我的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

useRef 有两个作用

  1. 获取DOM 元素
  2. 保存变量
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

useCallback缓存方法,第一参数是一个函数,第二个是一个数据,空数组表示只执行一次

监听页面大写

自定义Hook

自定义一个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

你可能感兴趣的:(JavaScript,react.js,javascript,前端)