文章目录
参考笔记: 链接
规则:
只在最顶层使用Hook;
只在React组件中才能调用。
useState
一般来说,在函数退出后变量就会”消失”,而 state 中的变量会被 React 保留
还可以用下面这种方法来设置state
setcount(()=>{
number+=1
return number
})
useEffect
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => { // 清除副作用( effect 的清除阶段在每次重新渲染时都会执行,而不是只在卸载组件的时候执行一次。)
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
},[]); // 设置依赖,当这个依赖的变化了,就会重新执行useEffect,如果传入一个空数组,就只会在挂载阶段执行一次
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
传入回调函数,且这个函数的返回值是一个函数,同时传入一个空数组。假如回调函数本身记为 A, 返回的函数记为 B,那么将在挂载阶段执行 A,卸载阶段执行 B
这里B函数的角色定位就类似于生命周期里 componentWillUnmount 方法里的逻辑
useEffect(()=>{
// 这里是 A 的业务逻辑
// 返回一个函数记为 B
return ()=>{
}
}, [])
useRef
获取DOM元素对象
import { useRef } from 'react'
function App () {
const username = useRef()
const handler = () => console.log(username) // { current: input}
return <input ref={username} onChange={handler} />
}
exporrt default App
操作组件
- 函数组件获取不了ref
- 给类组件绑定ref,获取组件的属性
-
- 给函数组件绑定ref,必须使用forwardRef 包裹,并且去获取子组件绑定ref的对象
import { forwardRef ,useRef} from "react";
//forwardRef获取子组件的Dom
const Forward = forwardRef((props,ref)=>{
return (
<div>
<div ref={ref}>富婆的快乐你不懂</div>
</div>
)
})
function App() {
const el = useRef(null)
return (
<div className="App">
<Forward ref={el}></Forward>
<button onClick={()=>{
console.log(el.current);
}}>获取子组件的Dom</button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
createContext && useContext 多层传递
用于向后代组件传递数据
外层组件
import React, { createContext } from 'react'
import ChildrenCom from "./childrenCom"
export const countContext = createContext();
export default function Home() {
return (
<countContext.Provider value={100}>
<h1>
context
</h1>
<ChildrenCom>
</ChildrenCom>
</countContext.Provider>
)
}
后代组件
import { useContext } from 'react'
import { countContext } from "../index"
export default function Home() {
const value = useContext(countContext);
return (
<>
<h1>
suncom--{value}
</h1>
</>
)
}
useMemo()
作用: 类似于Vue中的计算属性,可以监测某个值的变化,根据变化值计算新值。
useMemo会缓存计算结果,如果监测值没有发生变化,即使组件重新渲染,也不会重新计算,此行为可以有助于避免在每个渲染上进行昂贵的计算。
import React, { useState, useMemo } from 'react'
function App() {
const [count, setCount] = useState(0)
const result = useMemo(() => {
// 如果count值发生变化此函数重新执行
return count * 2
}, [count])
return (
<div>
<span>{count} {result}</span>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
)
}
export deefault App
useCallback && memo
主要是用于子组件修改父组件的值
useCallback 作用:性能优化,缓存函数,使用组件重新渲染时得到相同的函数实例。从而避免嵌套函数的再次渲染。
memo 作用:性能优化,如果本组件的数据没有发生变化,阻止组件更新
import React, { useState, memo, useCallback } from "react"
function App() {
const [count, setCount] = useState(0)
const resetCount = useCallback(() => setCount(0), [setCount])
return (
<div>
<span>{count}</span>
<button onClick={() => setCount(count + 1)}>+1</button>
<Foo resetCount={resetCount} />
</div>
)
}
const Foo = memo(function Foo(props) {
// memo包裹后,count发生变化,App组件重新渲染,但是子组件中的数据并未发生变化所以Foo不会被重新渲染
console.log("Foo组件重新渲染了")
return (
<div>
我是Foo组件
<button onClick={props.resetCount}>resetCount</button>
</div>
)
})
export default App
自定义hook并使用
在src下新建 hooks,hooks下新建 usePosition.jsx
- 必须使用use开头
import react,{useState,useEffect} from "react"
export default function usePosition(){
const[position,setPosition]=useState({x:0,y:0})
useEffect(()=>{
function getPosition(event){
setPosition({x:event.clientX,y:event.clientY})
}
document.addEventListener("click",getPosition)
return ()=>{
document.removeEventListener("click",getPosition)
}
})
return {
position,setPosition
}
}
组件内使用
import react,{useState,useEffect} from "react"
import usePosition from "../../hooks/usePosition"
export default ()=>{
const [number,setNumber]=useState(0)
useEffect(()=>{
document.title=`当前的number值为${number},我在第一次渲染和每次页面更新时都会执行`
})
const {position,setPosition } =usePosition()
return (
<div>
<div>
number---{number}
</div>
<div>
{position.x}---{position.y}
</div>
<button onClick={()=>{setNumber(number+1)}}>
+1
</button>
</div>
)
}
hooks下处理表单(处理受控组件)
import React, { useState } from "react";
import "./styles.css";
function Form() {
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
return (
<form>
<input
value={firstName}
onChange={e => setFirstName(e.target.value)}
placeholder="First name"
type="text"
name="firstName"
required
/>
<input
value={lastName}
onChange={e => setLastName(e.target.value)}
placeholder="Last name"
type="text"
name="lastName"
required
/>
<input
value={email}
onChange={e => setEmail(e.target.value)}
placeholder="Email address"
type="email"
name="email"
required
/>
<input
value={password}
onChange={e => setPassword(e.target.value)}
placeholder="Password"
type="password"
name="password"
required
/>
<button type="submit">Submit</button>
</form>
);}
export default Form;
表单数据收集示例
import { useState } from "react"
import "./index.css"
export default function Form(){
const [username,setUsername]=useState("")
async function handleForm(e){
e.preventDefault()
// 建议使用 ref 来获取
let dizhi=radioCheck(document.querySelectorAll(".dizhi"))
let xins=chk(document.getElementsByName('xin'))
let renwu=document.querySelector("#renwu").value
let upfile=await collectImg(document.querySelector("#fileUp"))
console.log(upfile)
// console.log({username,dizhi,xins,renwu})
}
// 收集单选框数据
function radioCheck(ele){
let radios=ele
let value = "";
for(let i=0;i<radios.length;i++){
if(radios[i].checked == true){
value = radios[i].value;
}
}
return value
}
// 收集复选框数据
function chk(ele){
let obj=ele;
let s='';
for(let i=0; i<obj.length; i++){
if(obj[i].checked) s+=obj[i].value+',';
}
return s
}
// 收集上传的图片信息
function collectImg(ele1) {
return new Promise((res, rej) => {
var v = ele1.value;
var reader = new FileReader();
reader.readAsDataURL(ele1.files[0]);
// 获取原来文件名
let oldfilename = ele1.files[0].name || ''
let strs = v.split('.');
var suffix = strs[strs.length - 1];
// 判断文件格式
if (suffix != 'jpg' && suffix != 'gif' && suffix != 'jpeg' && suffix != 'png') {
alert("你选择的不是图片,请选择图片!", 'error');
var obj = document.getElementById('fileUp');
obj.outerHTML = obj.outerHTML; //这样清空,在IE8下也能执行成功
return false
}
// 判断文件的大小 小于2M
if (ele1.files[0].size / 1000 > 2 * 1024) {
alert("上传文件不能大于2M", 'error')
return false
}
reader.onload = function (e) {
// if (pre) {
// if (ele2 !== null) {
// $(ele2).hide()
// }
// $(ele3).empty();
// let img1 = `
// <img class="userheader" src="${e.target.result}" alt="">`
// $(ele3).append(img1)
// }
res({
oldfilename,
imgdata:e.target.result
})
};
})
}
return (
<form action="#">
<label htmlFor="username" className="item">
<input type="text" value={username} onChange={e=>setUsername(e.target.value)} name="username" id="username" placeholder="username" />
</label>
<div className="item">
<input type="radio" name="dizhi" value="广州" className="dizhi" />广州
<input type="radio" name="dizhi" value="上海" className="dizhi" />上海
<input type="radio" name="dizhi" value="北京" className="dizhi" />北京
</div>
<div className="item">
<input type="checkbox" name="xin" id="" value="李" className="xin" />李
<input type="checkbox" name="xin" id="" value="张" className="xin" />张
<input type="checkbox" name="xin" id="" value="魏" className="xin" />魏
</div>
<div className="item">
// 这儿 也可以 使用上面的 username 方式来获取
<select name="renwu" id="renwu">
<option value="">
请选择
</option>
<option value="东邪">
东邪
</option>
<option value="西毒">
西毒
</option>
</select>
</div>
<div className="item">
<input type="file" name="" id="fileUp" />
</div>
<button onClick={handleForm}>提交</button>
</form>
)
}
其他Hook
useReducer()
作用:另一种让函数组件保存状态的方式
使用细节:和redux的使用类似
import React, { useReducer } from 'react'
function App() {
function reducer (state, action) {
switch (action.type) {
case 'increment':
return state + 1;
case 'decreament':
return state - 1;
default:
return state;
}
}
}
const [ count, dispatch ] = useeReducer(reducer, 0)
return (
<div>
<button onclick={() => dispatch({type: 'decrement'})}>-1</button>
<span>{count}</span>
<button onclick={() => dispatch({type: 'increment'})}>-1</button>
</div>
)
export default App;
useCallback()
作用:性能优化,缓存函数,使用组件重新渲染时得到相同的函数实例。从而避免嵌套函数的再次渲染。
使用场景
- 父组件向子组件传递函数,使用useCallback,子组件使用memo
import React, { useState, memo, useCallback } from "react"
function App() {
const [count, setCount] = useState(0)
const resetCount = useCallback(() => setCount(0), [setCount])
return (
<div>
<span>{count}</span>
<button onClick={() => setCount(count + 1)}>+1</button>
<Foo resetCount={resetCount} />
</div>
)
}
const Foo = memo(function Foo(props) {
// memo包裹后,count发生变化,App组件重新渲染,但是子组件中的数据并未发生变化所以Foo不会被重新渲染
console.log("Foo组件重新渲染了")
return (
<div>
我是Foo组件
<button onClick={props.resetCount}>resetCount</button>
</div>
)
})
export default App
useImperativeHandle
useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用 ref 这样的命令式代码。useImperativeHandle 应当与 forwardRef 一起使用:
useLayoutEffect
其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。
尽可能使用标准的 useEffect 以避免阻塞视觉更新。
其他
rmc // 快速创建组件(前提要先安装插件)