# React API
序号 | 分类 | API | 描述 |
---|---|---|---|
1 | 组件类 | Component | |
2 | PureComponent | 纯函数 | |
3 | memo | 纯函数 | |
4 | forwardRef | 用于 隔代 ref 获取引用。 | |
5 | lazy | 结合 Suspense 实现动态(异步)加载组件 | |
6 | Suspense | Suspense 让组件“等待”某个异步操作(数据、组件、图片、脚本等),直到该异步操作结束即可渲染。 | |
7 | Fragment | 碎片概念,用于 React 返回多个元素。默认 react 是不允许返回多个元素节点的,即一个根元素。 | |
8 | Profiler | 一般用于开发阶段,性能检测,检测一次 react 组件渲染用时,性能开销。 | |
9 | StrictMode | 严格模式,用于检测react项目中的潜在的问题。严格模式检查仅在开发模式下运行;它们不会影响生产构建。 | |
10 | 工具类 | createElement | 创建 React 元素。每个 JSX 元素都是调用 React.createElement() 的语法糖。 |
11 | cloneElement | 克隆 React 元素。 | |
12 | createContext | 可用于层层传递 value 。 | |
13 | createFactory(已废弃) | 此辅助函数已废弃,建议使用 JSX 或直接调用 React.createElement() 来替代它。 | |
14 | createRef | createRef 可以创建一个 ref 元素,附加在 react 元素上。 | |
15 | isValidElement | 验证对象是否为 React 元素,返回值为 true 或 false 。适用于公共组件或是开源库。 | |
16 | Children | 提供了用于处理 this.props.children 不透明数据结构的实用方法。 | |
17 | Hooks | useState | 返回一个 state ,以及更新 state 的函数。 |
18 | useEffect | 可以用作数据交互,或是生命周期。 | |
19 | useLayoutEffect | 同 useEffect。 | |
20 | useMemo | useMemo 接受两个参数,第一个参数是一个函数,返回值用于产生保存值。 | |
21 | useCallback | 返回一个 memoized 回调函数。 | |
22 | useRef | 可以用来获取 dom 元素,或者 class 组件实例。或保存一些数据 | |
23 | useReducer | useState 的替代方案。 | |
24 | useContext | useContext 可以代替 context.Consumer 来获取 Provider 中保存的 value 值。 | |
25 | useImperativeHandle | 可以让你在使用 ref 时自定义暴露给父组件的实例值。 | |
26 | useDebugValue | useDebugValue 可用于在 React 开发者工具中显示自定义 hook 的标签。 | |
27 | useTransition | 允许延时由 state 改变而带来的视图渲染。这个 api 目前处于实验阶段,没有被完全开放出来。 | |
28 | ReactDOM | render | 用于渲染一个 react 元素,一般 react 项目我们都用它,渲染根部容器 app 。 |
29 | hydrate | 服务端渲染用 hydrate 。用法与 render() 相同 | |
30 | createPortal | Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。例如弹窗 | |
31 | unstable_batchedUpdates | 批量更新 | |
32 | flushSync | 可以将回调函数中的更新任务,放在一个较高的优先级中。 | |
33 | findDOMNode | 用于访问组件DOM元素节点,不推荐 | |
34 | unmountComponentAtNode | 从 DOM 中卸载组件,会将其事件处理器和 state 一并清除。 |
# 组件类
# Component
# PureComponent
纯组件:浅比较 props
和 state
,用于 性能优化,减少 render
次数
使用场景:data中只有一层基本类型数据
# memo
React.memo (opens new window)为高阶组件,用于 性能优化。仅对 props
进行浅比较,当 state 或 context 发生变化时,它仍会重新渲染。如果想控制对复杂对象,可以通过传入第二个参数实现。
React.memo: 第二个参数 返回 true 组件不渲染 , 返回 false 组件重新渲染。
shouldComponentUpdate: 返回 true 组件渲染 , 返回 false 组件不渲染。
import React, { useState } from "react";
// 子组件
const TextDemo = (props) => {
return <div>number: {props.number}</div>;
};
const NewTextDemo = React.memo(TextDemo, (prev, next) => {
if (next.number === prev.number) return true; // 数据相同时,不渲染
if (next.number > 5) return true; // 数据大于5时,不渲染
return false; // 否则,渲染组件
});
// 父组件
const Memo = () => {
const [number, setNumber] = useState(0);
return (
<>
<NewTextDemo number={number} />
<button onClick={() => setNumber(number + 1)}>number++</button>
<button onClick={() => setNumber(number - 1)}>number--</button>
</>
);
};
export default Memo;
# forwardRef
用于 隔代 ref
获取引用。
# 1. 转发refs到DOM组件
import React, { useEffect, useRef } from "react";
// 孙组件
const Son = (props) => {
const { grandRef } = props;
return <div ref={grandRef}>这个是想要获取的元素</div>;
};
// 父组件
const Father = (props) => {
return (
<div>
<Son grandRef={props.grandRef} />
</div>
);
};
const NewFather = React.forwardRef((props, ref) => (
<Father grandRef={ref} {...props} />
));
// 爷组件
const GrandFather = (props) => {
const ref = useRef();
useEffect(() => {
console.log(ref.current);
}, []);
return <NewFather ref={ref} />;
};
export default GrandFather;
# 高阶组件转发refs
React.forwardRef (opens new window)
import React, { useRef, useEffect } from "react";
// 高阶组件
const HOC = (Component) => {
class Wrap extends React.Component {
render() {
const { forwardedRef, ...otherprops } = this.props;
return <Component ref={forwardedRef} {...otherprops} />;
}
}
return React.forwardRef((props, ref) => (
<Wrap forwardedRef={ref} {...props} />
));
};
// 子组件 - 使用高阶组件
class Child extends React.Component {
componentDidMount() {
console.log(666);
}
render() {
return <div>hello,world</div>;
}
}
const HocChild = HOC(Child, true);
// 父组件
const ForwardRefHOC = () => {
const node = useRef(null);
useEffect(() => {
/* 就可以跨层级,捕获到 Child 组件的实例了 */
console.log(node.current.componentDidMount);
}, []);
return <HocChild ref={node} />;
};
export default ForwardRefHOC;
# lazy
结合 Suspense
实现动态(异步)加载组件
import React from "react";
// 子组件
const Test = () => (
<img src="https://picsum.photos/id/6/300/200" alt="img" />
);
// 父组件
const LazyComponent = React.lazy(
() =>
new Promise((resolve) => {
setTimeout(() => {
resolve({
default: () => <Test />
});
}, 2000);
})
);
// 调用
class Lazy extends React.Component {
render() {
return (
<div className="context_box" style={{ marginTop: "50px" }}>
<React.Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</React.Suspense>
</div>
);
}
}
export default Lazy;
# Suspense
Suspense
让组件“等待”某个异步操作(数据、组件、图片、脚本等),直到该异步操作结束即可渲染。
import React from "react";
// 该组件是动态加载的
const OtherComponent = React.lazy(() => import("./Lazy"));
const Suspense = () => {
return (
// 显示 <Spinner> 组件直至 OtherComponent 加载完成
<React.Suspense fallback={<div>Suspense Loading...</div>}>
<OtherComponent />
</React.Suspense>
);
};
export default Suspense;
# Fragment
碎片概念,用于 React
返回多个元素。默认 react 是不允许返回多个元素节点的,即一个根元素。
Fragment
可以支持 key
属性。<></>
不支持 key
属性。
温馨提示:我们通过 map
遍历后的元素,react
底层会处理,默认在外部嵌套一个 <Fragment>
。
import React from 'react';
export default () => {
return (
<React.Fragment>
<li> 🍎🍎🍎 </li>
<li> 🍌🍌🍌 </li>
<li> 🍇🍇🍇 </li>
</React.Fragment>
)
}
import React from 'react';
export default () => {
return (
<>
<li> 🍎🍎🍎 </li>
<li> 🍌🍌🍌 </li>
<li> 🍇🍇🍇 </li>
</>
)
}
# Profiler
一般用于开发阶段,性能检测,检测一次 react
组件渲染用时,性能开销。
import React, { useEffect, useState } from "react";
const Profiler = () => {
const [number, setNumber] = useState(0);
useEffect(() => {
setTimeout(() => {
setNumber(1);
}, 2000);
}, []);
const callback = (...arg) => {
console.log(arg);
};
return (
<React.Profiler id="ProfilerTest" onRender={callback}>
<div>{number}</div>
</React.Profiler>
);
};
export default Profiler;
# StrictMode
严格模式,用于检测react项目中的潜在的问题。严格模式检查仅在开发模式下运行;它们不会影响生产构建。
StrictMode目前有助于:
- ①识别不安全的生命周期。
- ②关于使用过时字符串 ref API 的警告
- ③关于使用废弃的 findDOMNode 方法的警告
- ④检测意外的副作用
- ⑤检测过时的 context API
# 工具类
# createElement
创建 React
元素。每个 JSX
元素都是调用 React.createElement()
的语法糖。
React.createElement(
type,
[props],
[...children]
)
import React from "react";
const CreateElement = () => {
return React.createElement(
"div",
{ className: "parent" },
React.createElement(
"div",
{ className: "item" },
"\u751F\u547D\u5468\u671F"
),
React.createElement(React.Fragment, null, " Flagment"),
"text\u6587\u672C"
);
};
export default CreateElement;
# cloneElement
克隆 React
元素。
import React from "react";
const FatherComponent = ({ children }) => {
const newChildren = React.cloneElement(children, { age: 18 });
return <div> {newChildren} </div>;
};
const SonComponent = (props) => {
console.log(props);
return <div>hello,world</div>;
};
const CloneElement = () => {
return (
<div className="box">
<FatherComponent>
<SonComponent name="alien" />
</FatherComponent>
</div>
);
};
export default CloneElement;
# createContext
可用于层层传递 value
。
只有当组件所处的树中没有匹配到 Provider
时,其 defaultValue
参数才会生效。
import React from "react";
const defaultValue = { name: "xiaoli", mes: "默认值" };
const MyContext = React.createContext(defaultValue);
const ComponentA = (props) => {
const { name, mes } = props;
return (
<div>
<div> 姓名: {name} </div>
<div> 想对大家说: {mes} </div>
</div>
);
};
const ComponentB = () => {
/* 用 Consumer 订阅, 来自 Provider 中 value 的改变 */
return (
<MyContext.Consumer>
{(value) => <ComponentA {...value} />}
</MyContext.Consumer>
);
};
const CreateContext = () => {
const [value] = React.useState({
name: "alien",
mes: "let us learn React "
});
return (
<div style={{ marginTop: "50px" }}>
<MyContext.Provider value={value}>
<ComponentB />
</MyContext.Provider>
</div>
);
};
export default CreateContext;
# createFactory(已废弃)
此辅助函数已废弃,建议使用 JSX 或直接调用 React.createElement() 来替代它。
const Text = React.createFactory(()=><div>hello,world</div>)
const Index = () => {
return <div style={{ marginTop:'50px' }} >
<Text/>
</div>
}
# createRef
createRef
可以创建一个 ref
元素,附加在 react
元素上。
class Index extends React.Component{
constructor(props){
super(props)
this.node = React.createRef()
}
componentDidMount(){
console.log(this.node)
}
render(){
return <div ref={this.node} > my name is alien </div>
}
}
也可以不用,替代写法
class Index extends React.Component{
node = null
componentDidMount(){
console.log(this.node)
}
render(){
return <div ref={(node)=> this.node } > my name is alien </div>
}
}
或者函数组件中
function Index(){
const node = React.useRef(null)
useEffect(()=>{
console.log(node.current)
},[])
return <div ref={node} > my name is alien </div>
}
# isValidElement
验证对象是否为 React
元素,返回值为 true
或 false
。适用于公共组件或是开源库。
场景:验证容器组件的所有子组件,过滤到非react element类型。
const Text = () => <div>hello,world</div>
class WarpComponent extends React.Component{
constructor(props){
super(props)
}
render(){
return this.props.children.filter(item => React.isValidElement(item) ); // 过滤掉了最后一行纯文本
}
}
function Index(){
return <div style={{ marginTop:'50px' }} >
<WarpComponent>
<Text/>
<div> my name is alien </div>
Let's learn react together!
</WarpComponent>
</div>
}
# Children
提供了用于处理 this.props.children
不透明数据结构的实用方法。
# map
遍历 children
,并返回新数组。
class Text extends React.Component{
render(){
return <div>hello,world</div>
}
}
function WarpComponent(props){
const newChildren = React.Children.map(props.children,(item)=>item)
console.log(newChildren)
return newChildren
}
function Index(){
return <div style={{ marginTop:'50px' }} >
<WarpComponent>
{ new Array(3).fill(0).map(()=><Text/>) }
<span>hello,world</span>
</WarpComponent>
</div>
}
# forEach
仅遍历 children
。
function WarpComponent(props){
React.Children.forEach(props.children,(item)=>console.log(item))
return props.children
}
# count
Children.count
可以返回同一级别子组件的数量。
function WarpComponent(props){
const childrenCount = React.Children.count(props.children)
console.log('childrenCount: ', childrenCount); // childrenCount: 7
return props.children
}
function Index(){
return <div style={{ marginTop:'50px' }} >
<WarpComponent>
{ new Array(3).fill(0).map((item,index) => new Array(2).fill(1).map((item,index1) => <Text key={index+index1} />)) }
<span>hello,world</span>
</WarpComponent>
</div>
}
# toArray
Children.toArray
返回,props.children
扁平化后结果。
React.Children.toArray()
在拉平展开子节点列表时,更改 key
值以保留嵌套数组的语义。
# only
验证 children 是否只有一个子节点(一个 React 元素),如果有则返回它,否则此方法会抛出错误。
# Hooks
# useState
返回一个 state
,以及更新 state
的函数。
const [state, setState] = useState(initialState);
setState(newState);
# useEffect
可以用作数据交互,或是生命周期。
默认情况下,effect
将在每轮渲染结束后执行,但你可以选择让它 在只有某些值改变的时候
(传入第二个依赖数组参数) 才执行。
useEffect(() => {
const subscription = props.source.subscribe();
return () => {
// 清除订阅
subscription.unsubscribe();
};
}, [props]);
# useMemo
useMemo
接受两个参数,第一个参数是一个函数,返回值用于产生保存值。 第二个参数是一个数组,作为dep依赖项,数组里面的依赖项发生变化,重新执行第一个函数,产生新的值。
应用场景:
- 缓存一些值,避免重新执行上下文
const number = useMemo(()=>{
/** ....大量的逻辑运算 **/
return number
},[ props.number ]) // 只有 props.number 改变的时候,重新计算number的值。
- 减少不必要的dom循环
/* 用 useMemo包裹的list可以限定当且仅当list改变的时候才更新此list,这样就可以避免selectList重新循环 */
{
useMemo(() => (
selectList.map((i, v) => (
<span className={style.listSpan} key={v} >
{i.patentName}
</span>
))
), [selectList])
}
- 减少子组件渲染
/* 只有当props中,list列表改变的时候,子组件才渲染 */
const goodListChild = useMemo(()=> <GoodList list={ props.list } /> ,[ props.list ])
# useCallback
返回一个 memoized
回调函数。区别:useMemo
返回的是函数运行的结果, useCallback
返回的是函数。
/* 用react.memo */
const DemoChildren = React.memo((props) => {
/* 只有初始化的时候打印了 子组件更新 */
console.log("子组件更新");
useEffect(() => {
props.getInfo("子组件");
}, []);
return <div>子组件</div>;
});
const DemoUseCallback = ({ id }) => {
const [number, setNumber] = useState(1);
/* 此时usecallback的第一参数 (sonName)=>{ console.log(sonName) } 经过处理赋值给 getInfo */
const getInfo = useCallback(
(sonName) => {
console.log(sonName);
},
[id]
);
return (
<div>
{/* 点击按钮触发父组件更新 ,但是子组件没有更新 */}
<button onClick={() => setNumber(number + 1)}>增加</button>
<DemoChildren getInfo={getInfo} />
</div>
);
};
# useRef
useRef的作用:
- 是可以用来获取
dom
元素,或者class
组件实例。 - 创建
useRef
时候,会创建一个原始对象,只要函数组件不被销毁,原始对象就会一直存在,那么我们可以利用这个特性,来通过useRef
保存一些数据。
const DemoUseRef = () => {
const dom = useRef(null);
const handerSubmit = () => {
/* <div >表单组件</div> dom 节点 */
console.log(dom.current);
};
return (
<div>
{/* ref 标记当前dom节点 */}
<div ref={dom}>表单组件</div>
<button onClick={() => handerSubmit()}>提交</button>
</div>
);
};
# useLayoutEffect
同 useEffect。执行顺序不同:组件更新挂载完成 -> 执行 useLayoutEffect 回调-> 浏览器dom绘制完成。可能会阻塞浏览器的绘制
# useReducer
useState
的替代方案。使用场景:state
逻辑较复杂且包含多个子值,或者下一个 state
依赖于之前的 state
等。
const [state, dispatch] = useReducer(reducer, initialState, init);
const DemoUseReducer = () => {
/* number为更新后的state值, dispatchNumbner 为当前的派发函数 */
const [number, dispatchNumbner] = useReducer((state, action) => {
const { payload, name } = action;
/* return的值为新的state */
switch (name) {
case "add":
return state + 1;
case "sub":
return state - 1;
case "reset":
return payload;
}
return state;
}, 0);
return (
<div>
当前值:{number}
{/* 派发更新 */}
<button onClick={() => dispatchNumbner({ name: "add" })}>增加</button>
<button onClick={() => dispatchNumbner({ name: "sub" })}>减少</button>
<button onClick={() => dispatchNumbner({ name: "reset", payload: 666 })}>
赋值
</button>
{/* 把dispatch 和 state 传递给子组件 */}
<MyChildren dispatch={dispatchNumbner} State={{ number }} />
</div>
);
};
# useContext
useContext
可以代替 context.Consumer
来获取 Provider
中保存的 value
值。
/* 用useContext方式 */
const DemoContext = () => {
const value: any = useContext(Context);
/* my name is alien */
return <div> my name is {value.name}</div>;
};
/* 用Context.Consumer 方式 */
const DemoContext1 = () => {
return (
<Context.Consumer>
{/* my name is alien */}
{(value) => <div> my name is {value.name}</div>}
</Context.Consumer>
);
};
export default () => {
return (
<div>
<Context.Provider value={{ name: "alien", age: 18 }}>
<DemoContext />
<DemoContext1 />
</Context.Provider>
</div>
);
};
# useImperativeHandle
useImperativeHandle
可以让你在使用 ref
时自定义暴露给父组件的实例值。useImperativeHandle
应当与 forwardRef
一起使用,实现父组件操作子组件。
import React, {
useState,
useRef,
useImperativeHandle,
forwardRef
} from "react";
const FancyInput = (props, ref) => {
const [inputValue, setInputValue] = useState("");
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
changeValue: (value) => {
setInputValue(value);
}
}));
const handleChange = (e) => {
setInputValue(e.target.value);
};
return <input ref={inputRef} value={inputValue} onChange={handleChange} />;
};
const FancyInputDemo = forwardRef(FancyInput);
const UseImperativeHandle = () => {
const inputRef = useRef();
const handerClick = () => {
const { focus, changeValue } = inputRef.current;
focus();
changeValue(2222);
};
return (
<>
<FancyInputDemo ref={inputRef} />
<button onClick={handerClick}>操控子组件</button>
</>
);
};
export default UseImperativeHandle;
# useDebugValue
useDebugValue
可用于在 React
开发者工具中显示自定义 hook
的标签。
==提示:我们不推荐你向每个自定义 Hook 添加 debug 值。当它作为共享库的一部分时才最有价值。==
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
// ...
// 在开发者工具中的这个 Hook 旁边显示标签
// e.g. "FriendStatus: Online"
useDebugValue(isOnline ? 'Online' : 'Offline');
return isOnline;
}
# useTransition
useTransition
允许延时由 state
改变而带来的视图渲染。这个 api
目前处于实验阶段,没有被完全开放出来。
const SUSPENSE_CONFIG = { timeoutMs: 2000 };
function App() {
const [resource, setResource] = useState(initialResource);
const [startTransition, isPending] = useTransition(SUSPENSE_CONFIG);
return (
<>
<button
disabled={isPending}
onClick={() => {
startTransition(() => {
const nextUserId = getNextId(resource.userId);
setResource(fetchProfileData(nextUserId));
});
}}
>
Next
</button>
{isPending ? " 加载中..." : null}
<Suspense fallback={<Spinner />}>
<ProfilePage resource={resource} />
</Suspense>
</>
);
}
# ReactDOM
# render
用于渲染一个 react
元素,一般 react
项目我们都用它,渲染根部容器 app
。
import ReactDOM from "react-dom";
ReactDOM.render(< App / >, document.getElementById('app'));
# hydrate
服务端渲染用 hydrate
。用法与 render()
相同,但它用于在 ReactDOMServer
渲染的容器中对 HTML
的内容进行 hydrate
操作。
ReactDOM.hydrate(element, container[, callback])
# createPortal
Portal
提供了一种将子节点渲染到存在于父组件以外的 DOM
节点的优秀的方案。例如弹窗
接受两个参数,第一个: child 是任何可渲染的 React 子元素 第二个: container是一个 DOM 元素。
ReactDOM.createPortal(child, container)
function WrapComponent({ children }) {
const domRef = useRef(null);
const [PortalComponent, setPortalComponent] = useState(null);
React.useEffect(() => {
setPortalComponent(ReactDOM.createPortal(children, domRef.current));
}, []);
return (
<div>
<div className="container" ref={domRef}></div>
{PortalComponent}
</div>
);
}
class Index extends React.Component {
render() {
return (
<div style={{ marginTop: "50px" }}>
<WrapComponent>
<div>hello,world</div>
</WrapComponent>
</div>
);
}
}
# unstable_batchedUpdates
在 react-legacy
模式下,对于事件,react
事件有批量更新来处理功能,但是在一些非常规的事件中,批量更新功能会被打破。可以用 unstable_batchedUpdates
解决。
class Index extends React.Component {
constructor(props) {
super(props);
this.state = {
numer: 1
};
}
handerClick = () => {
Promise.resolve().then(() => {
ReactDOM.unstable_batchedUpdates(() => {
this.setState({ numer: this.state.numer + 1 });
console.log(this.state.numer);
this.setState({ numer: this.state.numer + 1 });
console.log(this.state.numer);
this.setState({ numer: this.state.numer + 1 });
console.log(this.state.numer);
});
});
};
render() {
return (
<div style={{ marginTop: "50px" }}>
<button onClick={this.handerClick}>click me</button>
</div>
);
}
}
# flushSync
flushSync
可以将回调函数中的更新任务,放在一个较高的优先级中。
import ReactDOM from "react-dom";
class Index extends React.Component {
state = { number: 0 };
handerClick = () => {
setTimeout(() => {
this.setState({ number: 1 });
});
this.setState({ number: 2 });
ReactDOM.flushSync(() => {
this.setState({ number: 3 });
});
this.setState({ number: 4 });
};
render() {
const { number } = this.state;
console.log(number); // 打印什么?? - 0 3 4 1
return (
<div>
<div>{number}</div>
<button onClick={this.handerClick}>测试flushSync</button>
</div>
);
}
}
# findDOMNode
findDOMNode
用于访问组件DOM元素节点,react
推荐使用 ref
模式,不期望使用findDOMNode。且 不能用于函数组件。
class Index extends React.Component {
handerFindDom = () => {
console.log(ReactDOM.findDOMNode(this));
};
render() {
return (
<div style={{ marginTop: "100px" }}>
<div>hello,world</div>
<button onClick={this.handerFindDom}>获取容器dom</button>
</div>
);
}
}
# unmountComponentAtNode
从 DOM
中卸载组件,会将其事件处理器和 state
一并清除。
function Text() {
return <div>hello,world</div>;
}
class Index extends React.Component {
node = null;
constructor(props) {
super(props);
this.state = {
numer: 1
};
}
componentDidMount() {
/* 组件初始化的时候,创建一个 container 容器 */
ReactDOM.render(<Text />, this.node);
}
handerClick = () => {
/* 点击卸载容器 */
const state = ReactDOM.unmountComponentAtNode(this.node);
console.log(state);
};
render() {
return (
<div style={{ marginTop: "50px" }}>
<div ref={(node) => (this.node = node)}></div>
<button onClick={this.handerClick}>click me</button>
</div>
);
}
}