1.useState 定义state,修改state
2.useEffect 用于实现挂载结束,更新结束,销毁阶段
3.useContext 用与实现跨组件通信
4.useRef 用于绑定组件和元素
5.useCallback 用于函数组件优化
6.useMemo 用于函数组件优化
7.useImPerativeHandle 搭配useRef来使用,用于获取子组件中定义的方法
8.useLayoutEffect 和useEffect一致,但是它在最外层组件使用
9.useReducer 状态管理,组件复用
useState
useState保存组件状态
import React, { useState } from 'react';
const ComponentUseState = () => {
const [num, setNum] = useState(0);
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount((count) => {
//依赖上一次的值做改变
return count - 1;
});
};
return (
<div>
<h2>Using useState</h2>
Number: {num}
<button onClick={() => setNum(num + 1)}>+</button>
{/* set是一个函数可以直接嗲用 */}
<button onClick={() => setNum(num - 1)}>-</button>
<br />
Count:{count}
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
};
export default ComponentUseState;
useEffect
useEffect可以实现组件的副作用
useEffect(希望执行的动作, [组件状态的列表]);
第二个参数用来处理useEffect调用的时机,是一个数组,数组内是组件状态的列表。
例如,想要实现每次点击计数器的同时更新网站的标题,那么useEffect的处理时机就应该是count state变化后,所以在第二个参数的列表中填入count。
useEffect(() => {
document.title = `点击了${count}次`;
}, [count]);
下文以该组件为例:
import React, { useState, useEffect } from "react";
const App = () => {
const [count, setCount] = useState(0);
const [list, setList] = useState([]);
useEffect(() => {
document.title = `点击了${count}次`;
}, [count]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => response.json())
.then((data) => setList(data));
}, []);
return (
<div>
<button
onClick={() => {
setCount(count + 1);
}}
>
Click
</button>
<span>count: {count}</span>
<div>
{list.map((item, index) => (
<span key={index}>{item.name}</span>
))}
</div>
</div>
);
};
export default App;
一、useEffect模拟componentDidMount
当useEffect的第二个参数传入空列表时,相当于模拟生命周期函数componentDidMount。这个副作用仅在组件第一次挂载ui的时候调用一次。
用它来模拟组件初次挂载时,访问api、获得数据:
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => response.json())
.then((data) => setList(data));
}, []);
二、useEffect模拟componentDidUpdate
如果在使用useEffect时不带上第二个参数,就相当于模拟了生命周期函数componentDidUpdate。每次渲染结束的时候都会被调用。
如果上面的例子去掉第二参数,像下面这样:
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => response.json())
.then((data) => setList(data));
});
1.在每次渲染后被执行,调用API更新list;
2.由于list的更新,ui会再次渲染;
3.程序就陷入了死循环。
因此,要谨慎使用第二个参数为空的情形。
三、useEffect模拟componentWillUnmount
在useEffect中返回一个函数用于模拟component WillUnMount
useEffect(() => {
let timerId = window.setInterval(() => {
console.log(Date.now())
}, 1000)
// 返回一个函数用于模拟 WillUnMount
return () => {
window.clearInterval(timerId)
}
}, [])
useContext
useContext主要用于函数组件之间传值的问题
App
import React, { createContext } from 'react';
import Navbar from './Navbar';
import Messages from './Messages';
export const TestContext = createContext({});
export default function App() {
return (
<TestContext.Provider
value={{
username: 'superawesome',
}}
>
<div className="test">
<Navbar />
<Messages />
</div>
</TestContext.Provider>
);
}
Navbar
import React, { useContext } from 'react';
import { TestContext } from './index';
const Navbar = () => {
const { username } = useContext(TestContext);
return (
<div className="navbar">
<p>{username}</p>
</div>
);
};
export default Navbar;
Messages
import React, { useContext } from 'react';
import {TestContext} from './index'
const Messages = () => {
const { username } = useContext(TestContext);
return (
<div className="messages">
<p>1 message for {username}</p>
</div>
);
};
export default Messages;
useRef
useRef创建一块引用
一、取dom元素
二、非受控组件
import React, { useRef } from 'react';
const App = () => {
const ipt = useRef();
const handleClick = () => {
console.log(ipt);
};
return (
<>
<h3>useRef</h3>
<input ref={ipt} />
<button onClick={handleClick}>获取dom</button>
</>
);
};
// function App() {
// //场景实现:获取用户进入页面修改后的count
// const [count, setCount] = useState(0);
// const num = useRef();
// num.current = count;
// useEffect(() => {
// setTimeout(() => {
// console.log(num);
// }, 3000);
// }, []);
// const handleClick = (number) => {
// setCount((count) => count + number);
// };
// return (
// <div>
// <p>You clicked {count} times</p>
// <button onClick={() => handleClick(1)}>增加 count</button>
// <button onClick={() => handleClick(-1)}>减少 count</button>
// </div>
// );
// }
export default App;
三、获取组件实例
import React, {
useRef,
useEffect,
useImperativeHandle,
forwardRef,
} from 'react';
// 函数组件可以接收第二个参数,是ref
function ChildInputComponent(props, ref) {
const inputRef = useRef(null);
const a = 123;
useImperativeHandle(ref, () => ({ input: inputRef.current, a }));
return (
<>
<div>{a}</div>
<input type="text" name="child input" ref={inputRef} />
</>
);
}
//类组件中如果需要使用ref获取组件实例,需要用React.forwardRef将组件包上;
const ChildInput = forwardRef(ChildInputComponent);
function App() {
const inputRef = useRef(null);
useEffect(() => {
console.log(inputRef);
}, []);
return (
<div>
<ChildInput ref={inputRef} />
</div>
);
}
export default App;
useCallback
// import React, { Component, PureComponent } from 'react';
// //因为父组件render,子组件也render,所以使用PureComponent防止子组件重新渲染
// class Child extends PureComponent {
// render() {
// console.log('child render');
// return <div>child</div>;
// }
// }
// class App extends Component {
// state = {
// count: 5,
// };
// obj = { fontSize: 20 };
// handleClick = () => {
// this.setState({ count: 5 });
// };
// render() {
// return (
// <>
// <h3>useCallback</h3>
// <Child />
// <p>{this.state.count}</p>
// <button onClick={this.handleClick}>+</button>
// </>
// );
// }
// }
// export default App;
import React, { useState, memo, useCallback } from 'react';
// memo是一个高阶组件,作用和PureComponent完全相同,用于函数组件
const Child = memo(({ fn }) => {
console.log('child render');
return (
<div>
child
{fn()}
</div>
);
});
const App = () => {
const [count, set_count] = useState(5);
const handleClick = () => {
set_count((count) => count + 1);
};
// useCallback是缓存函数, 需要和memo高阶组件一起用
// 作用就是将里面第一个参数的那个函数缓存起来
// 避免子组件的重新渲染
// 当这个函数要作为自定义事件往子组件里传的时候就要用
// 第二个参数是依赖数组
const fn = useCallback(() => {
console.log(count);
}, []);
return (
<>
<h3>useCallback</h3>
<Child fn={fn} />
<p>{count}</p>
<button onClick={handleClick}>+</button>
</>
);
};
export default App;
useMemo
import React, { useState, useMemo } from 'react';
const App = () => {
const [count, set_count] = useState(1);
const [num, set_num] = useState(10);
const fn = () => {
set_count((count) => count + 1);
};
const fn2 = () => {
set_num((num) => num + 1);
};
// useMemo也是用于函数缓存, 语法和useCallback一模一样
// useCallback缓存的是整个函数,useMemo缓存的是函数的返回值
// useMemo跟vue的计算属性是一样的
const sum = useMemo(() => {
console.log(123);
let s = 0;
for (let i = 0; i < count * 100; i++) {
s += i;
}
return s;
}, [count]);
return (
<>
<h3>useMemo</h3>
<p>{count}</p>
<button onClick={fn}>btn</button>
<p>{sum}</p>
<p>{num}</p>
<button onClick={fn2}>btn</button>
</>
);
};
export default App;
useReducer
useReducer主要处理状态更新的逻辑,并且脱离UI,可以复用
import React, { useReducer } from 'react';
//定义初始值
const initState = { count: 0 };
//state的处理函数
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return { count: state.count + action.payload };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
};
export default function App() {
//第一个参数satte的处理函数,第二个参数state初始值
//返回最新的state和dispatch函数
let [state, dispatch] = useReducer(reducer, initState);
return (
<>
<button onClick={() => dispatch({ type: 'increment', payload: 1 })}>
+
</button>
<span>{state.count}</span>
<button onClick={() => dispatch({ type: 'decrement', payload: 1 })}>
-
</button>
</>
);
}
- 如果你的state是一个数组或者对象
- 如果你的state变化很复杂,经常一个操作需要修改很多state
- 如果你希望构建自动化测试用例来保证程序的稳定性
- 如果你需要在深层子组件里面去修改一些状态
- 如果你用应用程序比较大,希望UI和业务能够分开维护