seState是允许你在React函数组件中添加state的Hook(钩子)
const [state,setState] = useState(initialvalue)
说明:
参数:state变量的初始值
返回值:数组,并且通过解构数组获取仅有的两个元素。第一个元素:state就是一个状态变量。第二个元素:setState是一个用于修改状态的函数。
代码:
import React,{useState} from 'react'
import {Slider} from 'antd-mobile';
//这里用到了antd-mobile,新手的话建议去官方文档
import './Demo.less'
export default function Demo() {
const [width, setWidth] = useState(100);
const [height,setHeight] = useState(100);
const [borderRadius,setBorderRadius] = useState(0);
// 行内样式对象
const style={
width:`${width}px`,
height:`${height}px`,
borderRadius:`${borderRadius}px`
}
return (
<div className='container'>
<p>width</p>
<Slider
value={width}
min={0}
max={300}
onChange={(val) => setWidth(val)}
/>
<p>height</p>
<Slider
value={height}
min={0}
max={300}
onChange={(val) => setHeight(val)}
/>
<p>radius</p>
<Slider
value={borderRadius}
min={0}
max={200}
onChange={(val) => setBorderRadius(val)}
/>
<div className="el" style={style}></div>
</div>
)
}
* {
touch-action: none;
}
.container{
margin: 0px 20px;
.el{
width: 100px;
height: 100px;
background-color: pink;
margin: 0 auto;
margin-top: 50px;
}
p{
margin-top: 20px;
}
}
Effect Hook 可以让你在函数组件中执行副作用操作。
语法:useEffect(callback,deps)
- callback:回调
- deps:依赖数组
import React,{useEffect, useState} from 'react'
export default function Demo() {
// 回调执行:
// DOM渲染完,执行一次useEffect的回调
// 每次更新之后也会执行useEffect的回调
// 每次我们重新渲染,都会生成新的effect,替换掉之前的
// 总结:某种意义上讲,effect更像时渲染结果的一部分--每个effect"属于"一次特定的渲染
const [count,setCount] = useState(5);
//格式 useEffect(callback,deps)
useEffect(() => {
console.log('effect')
document.title = `您点击了${count}次`
})
return (
<div>
<h1>Count:{count}</h1>
<button onClick={() => setCount(count+1) }>add</button>
</div>
)
}
import React,{useEffect, useState} from 'react'
export default function Demo() {
// 回调执行:
// DOM渲染完,执行一次useEffect的回调
// 每次更新之后也会执行useEffect的回调
// 每次我们重新渲染,都会生成新的effect,替换掉之前的
// 总结:某种意义上讲,effect更像时渲染结果的一部分--每个effect"属于"一次特定的渲染
const [count,setCount] = useState(0);
const [num,setNum] = useState(0);
//格式 useEffect(callback,deps)
useEffect(() =>{
console.log(`您点击了${count}次`);
},[count])
return (
<div>
<h1>Count:{count}</h1>
<button onClick={() => setCount(count+1) }>add</button>
<h1>Num:{num}</h1>
<button onClick={() => setNum(num+1) }>add</button>
</div>
)
}
由于useEffect()默认函数组件状态改变后,就执行,但是有时候我们需要定时器只在函数组件渲染后只运行一次。
import React, { useEffect, useState } from 'react'
export default function Demo() {
const [count,setCount] = useState(0);
//控制回调执行的次数:使用依赖数组
// -如果想让回调只执行一次,依赖数组写为空数组[]
useEffect(() => {
setInterval(() => {
console.log('定时器执行了')
}, 1000);
},[])
return (
<div>
<h1>Count:{count}</h1>
<button onClick={() => setCount(count + 1)}>add</button>
</div>
)
}
import React, { useEffect, useState } from 'react'
import axios from 'axios'
export default function Demo() {
const [count,setCount] = useState(0);
//控制回调执行的次数:使用依赖数组
// -如果想让回调只执行一次,依赖数组写为空数组[]
//这种写法是我们在初次使用时要注意的,hooks推荐我们使用下面的方法,不推荐直接将callback变成async函数
// useEffect(async() => {
// const result = await axios.get('https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata');
// console.log(result);
// },[])
useEffect(() => {
const request = async() =>{
const result = await axios.get('https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata');
console.log(result);
};
request();
},[])
return (
<div>
<h1>Count:{count}</h1>
<button onClick={() => setCount(count+1)}>add</button>
</div>
)
}
import React, { useEffect, useState } from 'react'
const Parent = () => {
const [isShow,setShow] = useState(true)
return (
<div>
<h1 onClick={() => setShow(!isShow)}>父组件</h1>
{
isShow ? <Demo/> : null
}
</div>
)
}
function Demo() {
useEffect(() => {
let time = setInterval(() => {
console.log('定时器开始了')
}, 1000);
// 返回的函数就是将要卸载的函数(子组件将要卸载时会触发)
// 卸载函数,清除定时器或其他的
return () => {
console.log('我要被卸载了');
clearInterval(time);
}
},[])
return (
<div>
子组件
</div>
)
}
export default Parent
那么在Hooks中,是如何清除(需要清除的)副作用操作的?
import React, { useEffect, useState } from 'react'
export default function Demo() {
const [count,setCount] = useState(0);
//控制回调执行的次数:使用依赖数组
useEffect(() => {
console.log('2-副作用逻辑')
let timer = setInterval(() => {
console.log(`定时器执行了,count为:${count}`)
}, 1000);
return () => {
console.log('3-销毁函数')
clearInterval(timer);
}
},[count])
console.log('1-DOM渲染');
return (
<div>
<h1>Count:{count}</h1>
<button onClick={() => setCount(count+1)}>add</button>
</div>
)
}
import React, { useRef } from 'react'
export default function Demo() {
//useRef:调用返回一个ref对象
// 功能1:绑定DOM节点/React元素
const myRef = useRef();
const show = () => {
console.log(myRef.current.value);
}
return (
<div>
<input ref={myRef} type='text'/>
<button onClick={show}>点击获取</button>
</div>
)
}
通过保持可变变量来改造定时器
问题:在进行全局清除定时器时,遇到如果清除后,无法在页面render后在useEffect中再次调用。
解决具体步骤:
1.引入useRef
2.创建ref对象 const myRefTimer = useRef()
3.把定时器的timerID保存到ref对象里面的current里面
4. 后面读取/清除 =>myRefTimer.current
import React, { useEffect, useRef, useState } from 'react'
export default function Demo() {
const [count,setCount] = useState(0);
//创建ref对象
const myRefTimer = useRef();
useEffect(() => {
myRefTimer.current = setInterval(() => {
console.log('定时器',count)
}, 1000);
return () => {
clearInterval(myRefTimer.current);
}
},[count])
return (
<div>
<h1>Count:{count}</h1>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => clearInterval(myRefTimer.current)}>点击按钮清除定时器</button>
</div>
)
}
/*Demo基本样式 */
.ref_container{
width: 375px;
height: 667px;
background-color: pink;
border-top: 1px solid pink;
}
.ref_el{
width: 50px;
height: 50px;
background-color: red;
margin-top: 150px;
}
//Demo函数组件
import React, { useEffect, useRef, useState } from 'react'
// 动画库,需要下载引入
import anime from 'animejs'
import './Demo.css'
export default function Demo() {
const elementRef = useRef();
const [isAnimate1,setAnimate1] = useState(false);
const [isAnimate2,setAnimate2] = useState(false);
//利用副作用函数执行动画
useEffect(() => {
isAnimate1 && ml_animate01()
},[isAnimate1])
useEffect(() => {
isAnimate2 && ml_animate02()
},[isAnimate2])
//第一个动画
const ml_animate01 = () => {
anime({
// 要做动画的元素
targets:elementRef.current,
translateX:300,
backgroundColor:'#ff8822',
borderRadius:['0%','50%'],
complete:() => {
setAnimate1(false);
setAnimate2(true);
}
})
}
const ml_animate02 = () => {
anime({
targets:elementRef.current,
translateX:0,
backgroundColor:'#f00',
borderRadius:['50%','0%'],
complete:() => {
setAnimate2(false);
}
})
}
const clickShow = () => {
//开启第一个动画
setAnimate1(true);
}
return (
<div className='ref_container' onClick={clickShow}>
<div className="ref_el" ref={elementRef}>
</div>
</div>
)
}
const [state,dispatch] = useReducer(reducer,initial);
语法说明:
import React, { useReducer } from 'react'
const reducer = (preState,action) => {
const {type,data} = action;
switch (type) {
case 'increment':
return preState + 1;
case 'decrement':
return preState - 1;
default:
return preState;
}
}
export default function Demo() {
const [count,dispatch] = useReducer(reducer,0);
return (
<div>
<h1>Count:{count}</h1>
<button onClick={() => dispatch({type:'increment'})}>+1</button>
<button onClick={() => dispatch({type:'decrement'})}>-1</button>
</div>
)
}
import React, { useReducer } from 'react'
//初始值
let initState = {count:4}
const reducer = (preState,action) => {
const {type,data} = action;
switch (type) {
case 'increment':
return {count:data + 1};
case 'decrement':
return {count:data - 1};
default:
return preState;
}
}
export default function Demo() {
const [state,dispatch] = useReducer(reducer,initState)
return (
<div>
<h1>Count:{state.count}</h1>
<button onClick={() => dispatch({type:'increment',data:state.count})}>+1</button>
<button onClick={() => dispatch({type:'decrement',data:state.count})}>-1</button>
</div>
)
}
const value = useContext(MyContext);
说明:
- 参数:一个context对象
- 返回值:返回Provider提供的value值
import React, { useContext, useState } from 'react'
// context 在React中,解决了一个跨多层组件传递数据的问题
//使用context第一步 :创建 context对象
const myContext = React.createContext();
//父组件
const Parent = () => {
const [color,setColor] = useState('red');
{/* 第二步:使用context 里面的 Provider组件包裹着要接收数据的组件 */}
return (
<myContext.Provider value={color}>
<div onClick={() => setColor('blue')}>
<h1>父组件</h1>
<Child/>
</div>
</myContext.Provider>
)
}
//子组件
const Child = () => {
return (
<>
<h1>子组件</h1>
<Sun/>
</>
)
}
//孙组件
const Sun = () => {
//第三步:通过useContext 来接收
//参数:myContext
//返回值:就是provider提供的数据
let data = useContext(myContext);
console.log(data)
return (
<div>
<h1 style={{color:data}}>孙组件</h1>
</div>
)
}
export default Parent
该例子将文件模块化,这个例子不算大,没有考虑样式,只有简单的样式,文件会有些多。下面看一下案例的功能动图:
Demo.css:
.container{
width:500px;
margin:0 auto;
}
.container ul li span {
display: inline-block;
width: 143px;
user-select: none;
}
Demo.jsx:
import React from 'react'
import TodoForm from './TodoForm'
import TodoList from './TodoList'
import TodoFooter from './TodoFooter'
import { MyProvider } from './context'
import './Demo.css'
export default function Demo() {
return (
<div className='container'>
<MyProvider>
<TodoForm/>
<TodoList/>
<TodoFooter/>
</MyProvider>
</div>
)
}
context.js:
import React, { useReducer } from "react";
import {reducer1, reducer2} from './reducer'
const initValue = [
{
id:1,
name:'吃饭',
done:false
},
{
id:2,
name:'睡觉',
done:false
}
]
export const MyContext = React.createContext();
export const MyProvider = ({children}) =>{
let [list,dispatch1] = useReducer(reducer1,initValue);
const [filter,dispatch2] = useReducer(reducer2,'all');
//根据过滤条件筛选我们希望的数据
switch (filter) {
case 'all':
list = list;
break;
case 'active':
list = list.filter(item => !item.done);
break;
case 'completed':
list = list.filter(item => item.done);
default:
break;
}
const value = {
list,
dispatch1,
filter,
dispatch2
}
return(
<MyContext.Provider value={value}>{children}</MyContext.Provider>
)
}
reducer.js:
export const reducer1 = (preState,action) => {
const {type,data} = action;
switch (type) {
case 'add':
return [...preState,{id:Date.now(),name:data,done:false}];
case 'del':
return preState.filter((item) => item.id !== data);
//切换任务是否完成
case 'toggle':
let newState = [...preState];
let todo = newState.find(item =>item.id === data);
todo.done = !todo.done;
return newState;
default:
return preState;
}
}
export const reducer2 = (preState,action) => {
const {type,data} = action;
switch (type) {
case 'filter':
return data;
default:
return preState;
}
}
TodoForm.jsx:
import React, { useContext, useRef } from 'react'
import { MyContext } from './context';
export default function TodoForm() {
const {dispatch1} = useContext(MyContext);
const inputRef = useRef();
const addTodo = () => {
const {value} = inputRef.current;
if(value)
dispatch1({type:'add',data:value});
inputRef.current.value = '';
}
return (
<div>
<input type='text' ref={inputRef}/>
<button onClick={addTodo}>添加</button>
</div>
)
}
TodoList.jsx:
import React,{useContext} from 'react'
import { MyContext } from './context'
export default function TodoList() {
const {list,dispatch1} = useContext(MyContext)
return (
<div>
<ul>
{
list.map((item) => {
return (
<li key={item.id}>
<span style={{textDecoration:item.done ? 'line-through' : 'none'}} onClick={() => dispatch1({type:'toggle',data:item.id})}>{item.name}</span>
<button onClick={ () => dispatch1({type:'del',data:item.id})}>×</button>
</li>
)
})
}
</ul>
</div>
)
}
TodoFooter.jsx:
import React, { useContext } from 'react'
import { reducer2 } from './reducer'
import { MyContext } from './context'
export default function TodoFooter() {
const {filter,dispatch2} = useContext(MyContext);
return (
<div>
<button disabled={filter === 'all'} onClick={() => dispatch2({type:'filter',data:'all'})}>ALL</button>
<button disabled={filter === 'active'} onClick={() => dispatch2({type:'filter',data:'active'})}>ACTIVE</button>
<button disabled={filter === 'completed'} onClick={() => dispatch2({type:'filter',data:'completed'})}>COMPLETED</button>
</div>
)
}
const memoizedValue = useMemo(() => fn(a,b),deps[a,b]);
语法说明:
它接收两个参数:
第一个参数为回调函数(计算过程fn,必须返回一个结果)。
第二个参数是依赖项(数组),当依赖项中某一个发生变化,结果将会重新计算。
注意:因为是缓存计算结果,所以参数1回调函数里面一定要有return返回值
import React, { useMemo, useState } from 'react'
export default function Demo() {
const [count,setCount] = useState(0);
const [other,setOther] = useState(0);
// 封装一个累加的函数
const calCount = (count) => {
console.log('重新计算了');
let _sum = 0;
for(let i = 1;i <= count; i ++){
_sum += i;
}
return _sum;
}
// 计算累加结果
//普通使用
// let sum = calCount(count);
//使用useMemo
let sum = useMemo(() => calCount(count),[count])
return (
<div>
<h1>Count-{count}-{sum}</h1>
<button onClick={() => setCount(count + 1)}>+1</button>
<h1>Other-{other}</h1>
<button onClick={() => setOther(other + 1)}>+1</button>
</div>
)
}
useCallback 可以让你在函数组件中缓存计算函数
const fnA = useCallback(fnB,[a]);
import React, { useCallback, useState } from 'react'
let set = new Set();
const Demo = ()=> {
const [count,setCount] = useState(0);
const [other,setOther] = useState(0);
let inputChange = e => {
console.log(e.target.value);
}
//缓存函数
// 依赖数组 依赖项是count ,所以只有count 不变,才会 缓存 inputChange函数
inputChange = useCallback(inputChange,[count])
//添加到set
set.add(inputChange)
console.log('长度',set.size)
return (
<div>
<input type='text' onChange={inputChange}/>
<button onClick={() => setCount(count + 1)}>{count}</button>
<button onClick={() => setOther(other + 1)}>{other}</button>
</div>
)
}
export default Demo;