一、react-hooks(useState、useRef、forwardRef)

发布于:2024-08-20 ⋅ 阅读:(149) ⋅ 点赞:(0)
一、useState

注意事项:

1.setState的方法并不像函数式组件会自动合并,所以需要我们通过展开运算符手动合并。

  // 更新方式1:展开运算符
        // const info={...userInfo,...data}
        // setUserInfo(info)
        // 方式2:
        setUserInfo(Object.assign({},userInfo)
        )

2.setState中的更新并不能拿到最新的值,如果需要可以通过在useEffect中获取更新后的值。

  但是这里也会有一个问题就是如果setState更新的动作也是在useEffect中操作,那可能也还是拿不到,可能就需要根据具体业务场景解决。

3.更新不及时的问题:

解决方式式通过函数式的方式进行赋值更新。函数体内可以实现想要的代码逻辑赋值,比如加两次等。

    const oddAdd=()=>{
        // 问题:只会加一次
        // setCount(count+1)
        // setCount(count+1)
        // 解决:
        setCount (()=>{
          return  count+1
        });
    }

4.useState严禁出现在代码块内,最好写到函数的起始位置方便阅读。

5.实现强制刷新。只需要给useState重新赋一次值即可。

 const [,forceUpdate]=useState({})
 const onRefresh=()=>{
        forceUpdate({})
    }
  return (
        <div className="App">
            <header className="App-header">
                <button onClick={onRefresh}>点我强制刷新</button>
            </header>
        </div>
    )

6.异步更改,多个状态的更改会合并,如果不能信任之前的状态,应该使用回调函数的方式改变状态。

二、useRef
作用:1.获取子组件或dom节点的实例对象;
import React,{useRef} from "react";
function App(){
   const inputRef=useRef(null)
    const onButtonClick=()=>{
        if(inputRef){
            inputRef.current.focus()
        }
    }
    return (
        <div className="App">
          <input type="text" ref={inputRef} />
          <button onClick={onButtonClick}>点击获得焦点</button>
        </div>
    )
}
export default App;
2.存储渲染周期之间的共享数据

利用useRef获取上一轮的props或状态

import React,{useState,useRef, useEffect} from "react";
function App(){
    const [count,setCount]=useState(0);
   // 定义一个ref实例对象
   const prevCountRef=useRef();
   // 将prevCountRef实例对象的值赋值给常量prevCount
   const prevCount=prevCountRef.current
   // useEffect中将任意元素中的值等于count的元素绑定为prevCountRef对象
   useEffect(()=>{
    (prevCountRef as any).current=count
   })
    return (
        <div className="App">
         <h1>现在的值:{count};之前的值:{prevCount}</h1>
          <button onClick={()=>setCount((count=>count+1))}>增加</button>
        </div>
    )

}
export default App;

 定时器存入useRef中,定时可id可以在整个组件内使用。(不知道怎么用的其实,没有明白使用场景)

import React,{useState,useRef, useEffect} from "react";
function App(){
   const intervalRel=useRef('')
   useEffect(()=>{
    const id=setInterval(() => {
        
    }, 500);
    intervalRel.current=id
    console.log(intervalRel)

    return ()=>{
        clearInterval(intervalRel.current)
    }
   })
    return (
        <div className="App">
        <h2>111</h2>
        </div>
    )

}
export default App;

useRef使用注意事项:

1、useRef的每次渲染,返回值都不变

2.ref.current发生拜年话并不会造成重新渲染

3.不可以在render里面更新ref.current的值

4.ref.current的值不可作为其他hooks的依赖项,因为ref是可变的,不会使页面再次渲染,react不会跟踪ref的变化。

5.不要在render函数中更新ref.current的值

三、forwardRef

需求:父组件中获取某个子组件中的input元素,然后设置使input获取焦点。

import React,{useState,useRef,forwardRef, useEffect} from "react";
// 子组件
const Child=forwardRef((props,ref)=>{
    console.log('props.ref==>',ref)
    return <>
    <input type="text" placeholder={props.placeholder} ref={ref}/>
    </>
})
// 父组件
function App(){
   const ref=useRef(null)
   const [placeholder,setPlaceHolder]=useState('请输入搜索内容')
   const onClick=()=>{
    console.log('获取到的子组件内容',ref.current.value)
    ref.current.focus()

   }
    return (
        <div className="App">
         <Child ref={ref} placeholder={placeholder}></Child>
         <button onClick={onClick}>获取子组件内容</button>
        </div>
    )

}
export default App;
四、useImperativeHandle

作用:控制ref的暴露颗粒度(----这里的用法优点问题,focus不知道为什么在父组件没有调用成功)

 useImperativeHandle(ref,()=>{
       return{
        focus:()=>(ref.current as HTMLInputElement).focus(),
        // value:(ref.current as HTMLInputElement).current

       }
    },[props.value])
五、useEffect

执行时间:组件首次渲染和每次状态更新之后执行,异步执行,此时dom更新完毕,不会阻塞dom渲染。

疑问:如果有一些操作是需要在更新之后请求接口,会出现闪屏问题吧?

注意事项:

1、第二个参数不能是引用类型,因为引用类型比较不出来数据的变化,会造成死循坏,也不推荐在useEffect中更改监听变量的值,即使不是引用类型。

2.useEffect中的清除函数在每次重新渲染的时候都会执行,而不是只在卸载组件的时候执行。

六、useLayoutEffect

执行时间:浏览器渲染之前,需要改变dom,并且需要改变页面样式的


function App(){
   const [num,setNum]=useState(0)
//    页面不会闪烁
    useLayoutEffect(()=>{
        if(num===0){
            const randomNum=10+Math.random()*200
            setNum(randomNum)
        }
    },[num])

    return (
        <div className="App">
        <div>{num}</div>
         <button onClick={()=>setNum(0)}>点击重新设置值</button>
        </div>
    )

}
export default App;
七、useReducer的使用

import React,{ useReducer} from "react";
import { fromReducer } from "../hooks/reducer/counter.ts";
// 父组件
function App(){
    // 注册实例
   const [formData,dispatch]=useReducer(fromReducer,{
    userName:'',
    userAge:'',
   })
   const onUpdateName=()=>{
    dispatch({
        type:'update',
        data:{
            userName:'鬼鬼',
            userAge:'19'
        }
    })
   }
    return (
        <div className="App">
        <div>名称:{formData.userName}</div>
       <div>年龄:{formData.userAge}</div>
       <button onClick={onUpdateName}>更新基本数据</button>
        </div>
    )

}
export default App;
案例:
初始案例代码:使用了useState管理状态

import React,{useState, useReducer, useEffect} from "react";
import { fromReducer } from "../hooks/reducer/counter.ts";
import resolve from "resolve";
// 模拟数据
const mockOrderList=[
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
]
const defaultPage={
    count:0,
    pageSize:10,
    pageNum:1
}
// 父组件
function App(){
    // 注册实例
//    const [formData,dispatch]=useReducer(fromReducer,{
//     userName:'',
//     userAge:'',
//    })
// 订单数据
    const [tableList,setTableList]=useState<any>([]);
    // 搜索关键词
    const [queryKey,setQueryKey]=useState('')
    // 请求状态
    const [loading,setLoading]=useState(false)
    const [page,setPage]=useState({...defaultPage})
    // 模拟查询订单接口
    const onQueryOrder=(data:any)=>{
    return new Promise((resolve,reject)=>{
        // 使用完setimeout后需要清除,以免避免内存浪费
        const timeObj=setTimeout(() => {
            resolve({
                list:mockOrderList,
                page:{
                    count:mockOrderList.length,
                    pageNum:data.pageNum,
                    pageSize:data.pageSize
                }
            })
            clearTimeout(timeObj)
        }, 1000);
    })
    }
    const onQuery=()=>{
    // 首先设置查询状态
    setLoading(true)
    // 请求获取数据的接口
    onQueryOrder({
        queryKey:queryKey,
        ...page
    }).then((res:any)=>{
        console.log(res)
        // 成功设置会先数据和修改查询状态
        setTableList(res.list)
        setPage(res.page)
        setLoading(false)
    }).catch(()=>{
        // 如果失败则清空数据和重置查询状态
        setTableList([])
        setPage({...defaultPage})
        setLoading(false)
    })
    }
    // 重置表单
    const onReset=()=>{
         setQueryKey('')
         onQuery();
    }
    useEffect(()=>{
        onQuery();
    },[]);
    return (
        <div className="App">
            {/*头部搜索表单  */}
            <p>
                <input placeholder="请输入搜索关键词" value={queryKey} onChange={(e)=>{setQueryKey(e.target.value)}}></input>
                <button onClick={onQuery}>搜索</button>
                <button onClick={onReset}>重置</button>
            </p>
            {/* 搜索结果部分 */}
            <table>
                <thead>
                    <tr>
                        <th>编号</th>
                        <th>商品名称</th>
                        <th>订单数量</th>
                        <th>用户名称</th>
                    </tr>
                </thead>
                <tbody>
                    {
                      tableList&&tableList.map((item:any,index:number)=>{
                            return <tr key={index}>
                                <td>{index+1}</td>
                                <td>{item.title}</td>
                                <td>{item.num}</td>
                                <td>{item.userName}</td>
                            </tr>
                        })
                    }
                </tbody>
            </table>
            {/* 表单分页部分 */}
            <p>当前分页信息:一共{page.count};每页:{page.pageSize};当前第:{page.pageNum}</p>
            <p>当前状态:{loading?'加载中':'加载完成'}</p>
        </div>
    )

}
export default App;
改造:使用reducer管理状态

注意事项:如果数据是数组对象,建议使用reducer管理,如果业务逻辑复杂,变化多,建议使用reducer.


import React,{useState, useReducer, useEffect} from "react";
// 模拟数据
const mockOrderList=[
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
    {title:'苹果手机6s',num:10,userName:'鬼鬼'},
]
const defaultPage={
    count:0,
    pageSize:10,
    pageNum:1
}
// 订单状态管理reducer
const orderReducer=(state:any,action:any)=>{
    switch (action.type){
        case 'query':
            return {
                ...state,
                tableList:[],
                loading:true
            }
        case 'success':
            return {
                tableList:action.tableList,
                page:action.page,
                loading:false
            }
         case 'error':
            return {
                tableList:[],
                page:{
                    ...defaultPage
                },
                loading:false
            }
        default:
            return state
    }
}
const initState={
    page:{
        count:0,
        pageSize:10,
    }
}
// 父组件
function App(){

    // 搜索关键词
    const [queryKey,setQueryKey]=useState<string>('')
    const [orderState,dispatch]=useReducer(orderReducer,initState)
    const {page,tableList,loading}=orderState
    /**模拟查询订单接口*/ 
    const onQueryOrder=(data:any)=>{
        return new Promise((resolve,reject)=>{
            // 使用完setimeout后需要清除,以免避免内存浪费
            const timeObj=setTimeout(() => {
                resolve({
                    list:mockOrderList,
                    page:{
                        count:mockOrderList.length,
                        pageNum:data.pageNum,
                        pageSize:data.pageSize
                    }
                })
                clearTimeout(timeObj)
            }, 1000);
        })
    }
    const onQuery=()=>{
    // 首先设置查询状态
        dispatch({type:'query'})
        onQueryOrder({queryKey:queryKey,...page}).then((res:any)=>{
        dispatch({
            type:'success',
            tableList:res.list,
            page:res.page
        })
    })}
    // 重置表单
    const onReset=()=>{
         setQueryKey('')
         onQuery();
    }
    useEffect(()=>{
        onQuery();
    },[]);
    return (
        <div className="App">
            {/*头部搜索表单  */}
            <p>
                <input placeholder="请输入搜索关键词" value={queryKey} onChange={(e)=>{setQueryKey(e.target.value)}}></input>
                <button onClick={onQuery}>搜索</button>
                <button onClick={onReset}>重置</button>
            </p>
            {/* 搜索结果部分 */}
            <table>
                <thead>
                    <tr>
                        <th>编号</th>
                        <th>商品名称</th>
                        <th>订单数量</th>
                        <th>用户名称</th>
                    </tr>
                </thead>
                <tbody>
                    {
                      tableList&&tableList.map((item:any,index:number)=>{
                            return <tr key={index}>
                                <td>{index+1}</td>
                                <td>{item.title}</td>
                                <td>{item.num}</td>
                                <td>{item.userName}</td>
                            </tr>
                        })
                    }
                </tbody>
            </table>
            {/* 表单分页部分 */}
            <p>当前分页信息:一共{page.count};每页:{page.pageSize};当前第:{page.pageNum}</p>
            <p>当前状态:{loading?'加载中':'加载完成'}</p>
        </div>
    )

}
export default App;

 


网站公告

今日签到

点亮在社区的每一天
去签到