react函数组件
创建
function MyComponent(){
return (
<div>
创建组件
</div>
)
}
useState 代替class组件state中的
function Parent() {
const [name, setName] = useState('小明')//useState,返回数据和修改数据的aip
return (
<div>
父组件的state:{name}
<button onClick={()=>setName('小黑')}>
改名字
</button>
</div>
)
}
//useState接受函数,setState也接受函数
function App() {
const [user, setName] = useState(() => ({ name: '用户名', age: 18 }))
return (
<div>
<p>{user.name}</p>
<button onClick={() => setName(state => ({ ...state, name: '新用户名' }))}>改名字</button>
</div>
)
}
useEffect 模拟生命周期
//不加第二个参数,任意数据发生变化都会执行
function Parent() {
const [N, setN] = useState(0)
useEffect(() => {
console.log('挂载后')
},[])//模拟componentDidMount(挂载后) 在useEffect第二个参数接[]
return (
<div>
父组件的state:{N}
<button onClick={() => setN(N + 1)}>
改名字
</button>
</div>
)
}
模拟组件消亡
function Parent() {
const [childVisible, setChildVisible] = useState(true)
useEffect(() => {
console.log('渲染')
return function () {//模拟componentWillUnmount (组件消亡前)在useEffect回调函数后
console.log('组件即将消亡')
}
})
const reverse = () => {
setChildVisible(!childVisible)
}
return (
<div>
{childVisible ? <button onClick={reverse}>show</button> : <button onClick={reverse}>hide</button>}
{childVisible ? <Child /> : ''}
</div>
)
}
const Child = props => {
return (
<div>
子组件
</div>
)
}
模拟componentDidUpdate
useEffect(() => {
console.log('数据更新l')
},[N])//模拟componentDidUpdata(数据更新了) 在useEffect第二个参数接[n],n每次变化会执行回调
//模拟componentDidUpdata 从第二次更新数据执行回调
//自定义Hooks
//模拟componentDidUpdate ,页面更新了执行回调
const [N, setN] = useState(0)//依赖n
//自定义Hook
const useX = (callback, dep) => {//接受一个,回调和依赖项
const [count, setCount] = useState(0)//用来计算渲染次数,排除第一次挂载页面
useEffect(() => {
setCount(x => x + 1)
}, [dep])//每次依赖发生变化,+1
useEffect(() => {//如果第二次渲染触发回调
if (count > 1) {
callback.call(null)
}
}, [count, callback])
}
useX(() => console.log('更新了'), N)//调用
useLayoutEffect
useLayoutEffect在浏览器渲染前之前执行
useLayoutEffect总是比useEffect先执行
useLayoutEffect里的人物最好影响了Layout
function App() {
const [n, setN] = useState(0)
useLayoutEffect(() => {
document.querySelector('#a').innerText = 'value:10000'
}, [n])
return (
<div>
xxxx
<p id="a">{n}</p>
</div>
)
}
useState 原理
函数式编程讲究数据不可变
所以我们每一次的数据都是不同的数据
useReducer
使用useReducer一般分为4部
- 创建初始值initialState
- 创建所有操作reducer(state,action)
- 传给useReducer,得到读和写的API
- 调用({type:'操作类型',data:data})
const initFormData = {
name: "",
age: 18,
nationality: "汉族"
}
const reducer = (state, action) => {
if (action.type === 'update') {
return { ...state, ...action.formData }
}
else if (action.type === 'reset') {
return initFormData
}
else {
return state
}
}
function App() {
const [state, dispatch] = useReducer(reducer, initFormData)
const onSubmit = (e) => {
console.log(e)
}
const xxx = () => console.log('xxx')
return (
<div>
<form onSubmit={onSubmit} onReset={xxx}>
{state.n}
<section>
姓名:<input type="text" value={state.name} onChange={
(e) => dispatch({ type: 'update', formData: { name: e.target.value } },)
} />
</section>
<section>
年龄:<input type="text" value={state.age} onChange={
(e) => dispatch({ type: 'update', formData: { age: e.target.value } },)
} />
</section>
<section>
名族:<input type="text" value={state.nationality} onChange={
(e) => dispatch({ type: 'update', formData: { nationality: e.target.value } })
} />
</section>
<div>
<button type="onSubmit">提交</button>
<button type='onReset'>重置</button>
</div>
</form>
</div>
)
}
使用useReducer代理Redux
- 将数据集中在一个store对象
- 将所有操作集中在reducer
- 创建一个Context
- 将第四步的内容放到第三步的Context
- 用Context.Provider将Context提供给所有组件
- 各个组件用useContext获取读写Api
//例子
import React, { useReducer, createContext, useContext, useEffect } from 'react'
const store = {//初始化仓库
user: null,
books: null,
movies: null
}
const reducer = (state, action) => {
switch (action.type) {
case 'setUser':
const data = { ...state, user: action.user }
console.log(data)
return data
case 'setBooks':
const data2 = { ...state, books: action.books }
console.log(data2)
return data2
case 'setMovies':
return { ...state, movies: action.movies }
default:
throw new Error()
}
}
const Context = createContext(null)
function App() {
const [state, dispatch] = useReducer(reducer, store)
return (
<Context.Provider value={{ state, dispatch }}>
<User />
<hr />
<Books />
<hr />
<Movies />
</Context.Provider>
)
}
function User() {
const { state, dispatch } = useContext(Context)
useEffect(() => {
ajax('user').then(res => {
console.log(res)
dispatch({ type: 'setUser', user: res })
})
}, [])
return (
<div>
用户名:{state.user ? state.user.name : '请稍等'}
</div>
)
}
function Books() {
const { state, dispatch } = useContext(Context)
useEffect(() => {
ajax('books').then(res => dispatch({ type: 'setBooks', books: res }))
}, [])
return (
<div>
<h2>books</h2>
{state.books ? state.books.map(item => <li key={item.id}>{item.name}</li>) : '请稍等'}
</div>
)
}
function Movies(){
const {state,dispatch} = useContext(Context)
useEffect(()=>{
ajax('movies').then(res=>dispatch({type:'setMovies',movies:res}))
},[])
return (
<div>
{state.movies ? state.movies.map(item => <li key={item.id}>{item.name}</li>) : '请稍等'}
</div>
)
}
function ajax(string) {
return new Promise((resolve, reject) => {
switch (string) {
case 'user':
setTimeout(() => {
resolve({
id: 1,
name: '小白'
})
}, 3000)
case 'books':
setTimeout(() => {
resolve([
{ name: '蝴蝶书', id: 1 },
{ name: '编程艺术', id: 2 }
])
}, 3000)
case 'movies':
setTimeout(() => {
resolve([
{
name: '两杆大烟枪',
id: 1
},
{
name: '小兵张嘎',
id: 2
}
])
}, 3000)
}
})
}
export default App
useMemo
React.memo可以模拟shouldeComponentUpdate
function App() {
const [n, setN] = useState(0)
const [m, setM] = useState(0)
const onClick = () => {
setN(state => state + 1)
}
const onClick2 = useMemo(() => {
return () => setM(state => state + 1)//m变化再次缓存结果
}, [m])//如果依赖m发生变化话才冲洗执行<M />组件的内容
return (
<div>
{n}
<hr />
<button onClick={onClick}>加N</button>
<M value={m} onClick={onClick2} />
</div>
)
}
const M = React.memo((props) => {
console.log('执行了')
console.log(props)
return (
<div>
{props.value}
<hr />
<button onClick={props.onClick}>加M</button>
</div>
)
})
//这样写有问题,如果传进来的是一个对象,或者一个函数(因为函数每次的地址不同)React,不会帮我们阻止多余的渲染,所以需要我们用useMemo,来设定当某个值发生变化了再执行
//或者用下面这种写法也可以
const MyComponent = React.memo(
_MyComponent,
(prevProps, nextProps) => nextProps.count !== prevProps.count//检查props
)
useMome语法糖useCallback
useCallback(x=>console.log(x),[依赖])
useMemo(()=>x=>console.log(x),[m])
两种写法等价
useRef
用法:如果我们需要一个值在组件不断render的时候保持不变
Ref不会自动render
function App() {
const [n, setN] = useState(0)
const count = useRef({ value: 0 })//创建
console.log(count)
const onClick = () => {
setN(state => state + 10)
}
useEffect(() => {
count.current.value += 1//记录n变化的次数
console.log(count.current.value)
}, [n])
return (
<div>
{n}
<hr />
<button onClick={onClick}>加N</button>
</div>
)
}
frowardRef实现函数组件ref
props无法传递ref属性,所以用frowardRef包裹住该组件
function App() {
const count = useRef()//创建
return (
<div>
{n}
<hr />
<Son ref={count} >加N</Son>
</div>
)
}
const Son = forwardRef((props, ref) => {
useEffect(() => {
console.log(props, ref)
}, [])
return (
<div ref={ref}>
xxx
</div>
)
})
useImperativeHandle (setRef)
//一般用于封装一个Ref
function App() {
const count = useRef(null)//创建
useEffect(() => {
console.log(count.current)
}, [])
return (
<div>
<Son ref={count} >加N</Son>
</div>
)
}
const Son = forwardRef((props, ref) => {
const realButton = createRef(null)
useImperativeHandle(ref, () => ({
x: () => realButton.current.remove(),//添加一些属性和功能
realButton//返回当前button
}))
return (
<div ref={realButton}>
xxx
</div>
)
})
自定义Hooks
export default (initState) => {
const [list, setList] = useState(initState)
useEffect(() => {
ajax('/list').then(res => setList(res))
}, [])
const deleteList = (deleteIndex) => {
setList(state => state.filter((item, index) => index !== deleteIndex))
}
return { list, setList, deleteList }
}
function ajax(path) {
return new Promise((resolve, reject) => {
if (path === '/list') {
setTimeout(() => {
resolve([
{ id: 1, name: '小明' },
{ id: 2, name: '小黑' },
{ id: 3, name: '杨小气' },
{ id: 4, name: '小仙女' },
{ id: 5, name: '杨狗蛋' }
])
})
}
})
}