React学习分享(六)

React Hooks

使用hooks理由

  1. 高阶组件为了复用,导致代码层级复杂
  2. 生命周期的复杂
  3. 写成functional组件,无状态组件 ,因为需要状态,又改成了class,成本高

一、useState(保存组件状态)

const [value, setvalue] = useState("")

相当于类组件中的状态值State

例子:

import React, {
    
     useState } from 'react'
export default function App() {
    
    
    const [value, setvalue] = useState("")
    const [list, setlist] = useState(["11", "22", "33"])
    const add = () => {
    
    
        setlist([...list, value])
        console.log(list);
        setvalue("")
    }
    const getValue = (e) => {
    
    
        console.log(e.target.value);
        setvalue(e.target.value)
    }
    const del = (index) => {
    
    
        const newlist = [...list]
        newlist.splice(index, 1)
        setlist([...newlist])
    }
    return (
        <div>
            <input type="text" value={
    
    value} onChange={
    
    (e) => {
    
    
                getValue(e)
            }} />
            <button onClick={
    
    add}>添加</button>
            <ul>
                {
    
    
                    list.map(
                        (item, index) => {
    
    
                            return (
                                <li key={
    
    index}>{
    
    item}  <button onClick={
    
    () => {
    
    
                                    del(index)
                                }}>删除</button></li>
                            )
                        }
                    )
                }
            </ul>
            {
    
    
                !list.length && <div></div>
            }
        </div>
    )
}

在使用useState时,我们会结构出两个变量,第一个value是我们的状态,第二个setvalue是我们用来修改状态的唯一方法。

二、useEffect(处理副作用)

Function Component 不存在生命周期,所以不要把 Class Component 的生命周期概念搬过来试图对号入座。useEffect用来实现类似生命周期的。

    useEffect(() => {
    
    
      first
   
      return () => {
    
    
        second
      }
    }, [third])

useEfffect中接收两个参数,一个是回调函数,一个是数组,
回调函数中first位置可以写处理逻辑,比如发送请求,初始化,定时器(类似于类组件中componentsDidMount),在return中second位置可以做销毁定时器的操作(类似于componentsWillUnmount),第二个参数数组,放我们使用的依赖的状态也就是useState或者传入的props,如果传空数组的话就只能执行一次。

例子:

import React, {
    
     useState, useEffect } from 'react'
export default function App() {
    
    
    const [name, setname] = useState("xiaoming")
    const [password, setpassword] = useState("123456")
    useEffect(() => {
    
    
        setname(name.substring(0, 1).toUpperCase() + name.substring(1))
    }, [name])
   
    return (
        <div>
            App-{
    
    name} - {
    
    password}
            <button onClick={
    
    () => {
    
    
                setname("xiaoli")
            }}>click</button>
            <button onClick={
    
    () => {
    
    
                setpassword("qwertyu")
            }}>修改密码</button>
        </div>
    )
}

不要对依赖撒谎, 如果你明明使用了某个变量,却没有申明在依赖中,你等于向React撒了谎,后果就是,当依赖的变量改变时,useEffect 也不会再次执行, eslint会报警告。

第一次执行一次后,当name跟新时,会再次更新useEffect,需注意我们在useEffect中setname方法时一直传入新的会一直重新调用渲染页面(状态一直改变),这样会导致循环。

例子:

import React, {
    
     useState, useEffect } from 'react'
import axios from 'axios'
export default function App() {
    
    
    const [type, settype] = useState(1)
    return (
        <div>
            <ul>
                <li onClick={
    
    () => {
    
    
                    settype(1)
                }}>正在热映</li>
                <li onClick={
    
    () => {
    
    
                    settype(2)
                }}>即将上映</li>
            </ul>
            <FilmList type={
    
    type}></FilmList>
        </div>
    )
}
function FilmList(props) {
    
    
    const [list, setlist] = useState([])
    useEffect(() => {
    
    
        if (props.type === 1) {
    
    
            //请求卖座正在热映的数据
            console.log("请求卖座正在热映的数据")
            axios({
    
    
                url: "https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=6369301",
                headers: {
    
    
                    'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.0","e":"16395416565231270166529","bc":"110100"}',
                    'X-Host': 'mall.film-ticket.film.list'
                }
            }).then(res => {
    
    
                console.log(res.data.data.films)
                setlist(res.data.data.films)
            })
        } else {
    
    
            //请求卖座即将上映的数据
            console.log("请求卖座即将上映的数据")
            axios({
    
    
                url: "https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=2&k=8077848",
                headers: {
    
    
                    'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.0","e":"16395416565231270166529","bc":"110100"}',
                    'X-Host': 'mall.film-ticket.film.list'
                }
            }).then(res => {
    
    
                console.log(res.data.data.films)
                setlist(res.data.data.films)
            })
        }
    }, [props.type])  //当依赖里的变量更新时会被再次调用useEffect中的回调函数
    return (
        <ul>
            {
    
    
                list.map(item =>
                    <li key={
    
    item.filmId}>{
    
    item.name}</li>
                )
            }
        </ul>
    )
}

在这里也要说一下useEffect和useLayoutEffect有什么区别?

扫描二维码关注公众号,回复: 15238989 查看本文章

简单来说就是调用时机不同, useLayoutEffect 和原来 componentDidMount & componentDidUpdate 一致,在 react完成DOM更新后马上同步调用的代码,会阻塞页面渲染。而 useEffect 是会在整个页面渲染完才会调用的 代码。useLayoutEffect作用在创建DOM之后,渲染之前。

官方建议优先使用 useEffect

在实际使用时如果想避免页面抖动(在 useEffect 里修改DOM很有可能出现)的话,可以把需要操作DOM的代码放在useLayoutEffect里。在这里做点dom操作,这些dom修改会和react做出的更改一起被一次性渲染到屏幕 上,只有一次回流、重绘的代价。

三、useCallback(记忆函数)

防止因为组件重新渲染,导致方法被重新创建 ,起到缓存作用; 只有第二个参数 变化了,才重新声明一次

    const handleChange = useCallback(
        (evt) => {
    
    
            settext(evt.target.value)
        },
        [text]
    )

<input onChange={
    
    handleChange} value={
    
    text} />

//只有text改变后, 这个函数才会重新声明一次, 
//如果传入空数组, 那么就是第一次创建后就被缓存, 如果text后期改变了,拿到的还是老的text。 
//如果不传第二个参数,每次都会重新声明一次,拿到的就是最新的text.

四、useMemo 记忆组件

useCallback 的功能完全可以由 useMemo 所取代,如果你想通过使用 useMemo 返回一个记忆函数也是完全可以的。相当于Vue中的计算属性。

唯一的区别是:useCallback 不会执行第一个参数函数,而是将它返回给你,而 useMemo 会执行第一个函数并 且将函数执行结果返回给你。所以在前面的例子中,可以返回 handleClick 来达到存储函数的目的。

所以 useCallback 常用记忆事件函数,生成记忆后的事件函数并传递给子组件使用。而 useMemo 更适合经过函数计算得到一个确定的值,比如记忆组件。

import React, {
    
     useState,useEffect,useMemo } from 'react'
import axios from 'axios'
export default function Cinema(){
    
    
    const [mytext, setmytext] = useState("")
    const [cinemaList, setcinemaList] = useState([])
    useEffect(() => {
    
    
        axios({
    
    
            url:"https://m.maizuo.com/gateway?cityId=110100&ticketFlag=1&k=7406159",
            method:"get",
            headers:{
    
    
                'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.0.4","e":"16395416565231270166529","bc":"110100"}',
               
                'X-Host': 'mall.film-ticket.cinema.list'
            }
        }).then(res=>{
    
    
            setcinemaList(res.data.data.cinemas)
        })
    }, [])
    const getCinemaList = useMemo(() => cinemaList.filter(item=>item.name.toUpperCase().includes(mytext.toUpperCase()) ||
    item.address.toUpperCase().includes(mytext.toUpperCase())
    ), [cinemaList,mytext])
    return <div>
            {
    
    /* {this.state.mytext} */}
                <input value={
    
    mytext} onChange={
    
    (evt)=>{
    
    
                    setmytext(evt.target.value)
                }}/>
                {
    
    
                    getCinemaList.map(item=>
                        <dl key={
    
    item.cinemaId}>
                            <dt>{
    
    item.name}</dt>
                            <dd>{
    
    item.address}</dd>
                        </dl>    
                    )
                }
       
        </div>
}

useMemo和useCallback两者的区别

  1. useMemo 缓存的结果是回调函数中return回来的值,主要用于缓存计算结果的值,应用场景如需要计算的状态。
  2. useCallback 缓存的结果是函数,主要用于缓存函数,应用场景如需要缓存的函数,因为函数式组件每次任何一个state发生变化,会触发整个组件更新,一些函数是没有必要更新的,此时就应该缓存起来,提高性能,减少对资源的浪费;另外还需要注意的是,useCallback应该和React.memo配套使用,缺了一个都可能导致性能不升反而下降。

useRef(获取DOM节点或React元素)

相当于类组件中的 createRef

const myswiper = useRef(null);
<div ref={myswiper}></div>

通过 myswiper.current 获取引用的DOM对象
修改 myswiper.current 不会引发组件重新渲染
useRef 在每次重新渲染后都保持不变,而 createRef 每次都会发生变化

useContext

用法和类组件中的消费者和生产者类似,相当于Vue中的$bus

在根组件中使用 GlobalContext.Provider 组件包裹需要接收数据的后代组件,并通过 value 属性提供要共享的数据。

需要获取公共数据的后代组件:导入useContext,并按需导入根组件中导出的Context对象;调用useContext(第一步中导出的Context) 得到value的值。

案例:

import React, {
    
     useState, useEffect, useContext } from 'react'
import axios from 'axios'
import './css/index.css'
const GlobalContext = React.createContext() //创建context对象
export default function App() {
    
    
    const [filmList, setfilmList] = useState([])
    const [info, setinfo] = useState("")
    // 请求数据并更新
    useEffect(() => {
    
    
        axios.get(`/test.json`).then(res => {
    
    
            setfilmList(res.data.data.films)
        })
    }, [])
    return (
        <GlobalContext.Provider value={
    
    {
    
    
            call: "打电话",
            sms: "短信",
            info: info,
            changeInfo: (value) => {
    
    
                setinfo(value)
            }
        }}>
            <div>
                {
    
    
                    filmList.map(item =>
                        <FilmItem key={
    
    item.filmId} {
    
    ...item} ></FilmItem>
                    )
                }
                <FilmDetail ></FilmDetail>
            </div>
        </GlobalContext.Provider>
    )
}
/*受控组件*/
function FilmItem(props) {
    
    
    let {
    
     name, poster, grade, synopsis } = props
    const value = useContext(GlobalContext)
    return <div className="filmitem" onClick={
    
    () => {
    
    
        console.log(synopsis)
        value.changeInfo(synopsis)
    }}>
        <img src={
    
    poster} alt={
    
    name} />
        <h4>{
    
    name}</h4>
        <div>观众评分:{
    
    grade}</div>
    </div>
}
function FilmDetail() {
    
    
    const value = useContext(GlobalContext)
    return <div className="filmdetail">
        detail-{
    
    value.info}
    </div>
}

useReducer(组件内部的状态管理方法)

相当于redux ,Vue中的vuex

const [state, dispatch] = useReducer(reducer, intialState)

传入两个参数,reducer是一个函数,用来管理状态的,intialState是初始状态值的。
接收的两个值,state是我们初始化时的状态,dispatch是改变状态的唯一方法。

import React, {
    
     useReducer } from 'react'
// 处理函数
const reducer = (prevState, action) => {
    
    
    // preState是以前的状态值,action是你传过来的参数
    // 这里我们应该不能影响原状态,保留住原状态,所以需要新建一个状态
    let newstate = {
    
     ...prevState }
    switch (action.type) {
    
    
        case "jian":
            newstate.count--
            return newstate
        case "jia":
            newstate.count++
            return newstate
        default:
            return prevState
    }
}
// 外部对象
const intialState = {
    
    
    count: 0
}
export default function App() {
    
    
    const [state, dispatch] = useReducer(reducer, intialState)
    return (
        <div>
            <button onClick={
    
    () => {
    
    
                dispatch({
    
    
                    type: "jian"
                })
            }}>-</button>
            <div>{
    
    state.count}</div>
            <button onClick={
    
    () => {
    
    
                // 通过调用dispatch这个函数,会把我们的数据传入到 reducer
                dispatch({
    
    
                    type: "jia"
                })
            }}>+</button>
        </div>
    )
}

用法与 useState 类似,从 useReducer 中得到读接口 state,写接口 dispatch。最后操作时传参给 dispatch 写接口。操作灵活多变,比 useState 好处就是能聚集所有的操作和各种状态量。着重理解这几行代码,读懂。

useReducer 算是 useState 的复杂版

使用 useReducer 分以下步骤:

  1. 创建初始值的状态initialState
  2. 创建所有对状态的操作reducer(prevState,action)
  3. 传给useReducer,得到读和写的接口
  4. 调用写({‘type’:‘操作类型’})

通常我们使用 useReducer 和 useContext 配合使用
案例:

import React, {
    
     useEffect, useContext, useReducer } from 'react'
import axios from 'axios'
import './css/index.css'
const GlobalContext = React.createContext() //创建context对象
const intialState = {
    
    
   info: "",
   filmList: []
}
const reducer = (prevState, action) => {
    
    
   var newState = {
    
     ...prevState }
   switch (action.type) {
    
    
       case "change-filmlist":
           newState.filmList = action.value
           return newState
       case "change-info":
           newState.info = action.value
           return newState
       default:
           return prevState
   }
}
export default function App() {
    
    
   const [state, dispatch] = useReducer(reducer, intialState)
   useEffect(() => {
    
    
       axios.get(`/test.json`).then(res => {
    
    
           // console.log(res.data.data.films)
           dispatch({
    
    
               type: "change-filmlist",
               value: res.data.data.films
           })
       })
   }, [])
   return (
       <GlobalContext.Provider value={
    
    
           {
    
    
               state,
               dispatch
           }
       }>
           <div>
               {
    
    /* {this.state.info} */}
               {
    
    
                   state.filmList.map(item =>
                       <FilmItem key={
    
    item.filmId} {
    
    ...item} ></FilmItem>
                   )
               }
               <FilmDetail ></FilmDetail>
           </div>
       </GlobalContext.Provider>
   )
}
/*受控组件*/
function FilmItem(props) {
    
    
   let {
    
     name, poster, grade, synopsis } = props
   const {
    
     dispatch } = useContext(GlobalContext)
   // console.log(context)
   return <div className="filmitem" onClick={
    
    () => {
    
    
       console.log(synopsis)
       // this.props.onEvent(synopsis)
       // value.info = "2222222"
       // console.log(value)
       dispatch({
    
    
           type: "change-info",
           value: synopsis
       })
   }}>
       <img src={
    
    poster} alt={
    
    name} />
       <h4>{
    
    name}</h4>
       <div>观众评分:{
    
    grade}</div>
   </div>
}

自定义hooks

  1. 自定义 Hooks 是一个函数,约定函数名称必须以 use 开头,React 就是通过函数名称是否以 use 开头来判断是不是 Hooks。
  2. Hooks 只能在函数组件中或其他自定义 Hooks 中使用,否则,会报错!
  3. 自定义 Hooks 用来提取组件的状态逻辑,根据不同功能可以有不同的参数和返回值(就像使用普通函数一样)

例子:

import React, {
    
     useState,useEffect,useMemo } from 'react'
import axios from 'axios'
function useCinemaList(){
    
    
   const [cinemaList, setcinemaList] = useState([])
   useEffect(() => {
    
    
       axios({
    
    
           url:"https://m.maizuo.com/gateway?cityId=110100&ticketFlag=1&k=7406159",
           method:"get",
           headers:{
    
    
               'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.0.4","e":"16395416565231270166529","bc":"110100"}',
              
               'X-Host': 'mall.film-ticket.cinema.list'
           }
       }).then(res=>{
    
    
           setcinemaList(res.data.data.cinemas)
       })
   }, [])
   return {
    
    
       cinemaList
   }
}
function useFilter(cinemaList,mytext){
    
    
   const getCinemaList = useMemo(() => cinemaList.filter(item=>item.name.toUpperCase().includes(mytext.toUpperCase()) ||
   item.address.toUpperCase().includes(mytext.toUpperCase())
   ), [cinemaList,mytext])
   return {
    
    
       getCinemaList
   }
}
export default function Cinema(){
    
    
   const [mytext, setmytext] = useState("")
  
   const {
    
    cinemaList}  = useCinemaList()
   const {
    
    getCinemaList} = useFilter(cinemaList,mytext)
   return <div>
           {
    
    /* {this.state.mytext} */}
               <input value={
    
    mytext} onChange={
    
    (evt)=>{
    
    
                   setmytext(evt.target.value)
               }}/>
               {
    
    
                   getCinemaList.map(item=>
                       <dl key={
    
    item.cinemaId}>
                           <dt>{
    
    item.name}</dt>
                           <dd>{
    
    item.address}</dd>
                       </dl>    
                   )
               }
       </div>
}

猜你喜欢

转载自blog.csdn.net/weixin_46824709/article/details/126703281
今日推荐