社招前端react面试题整理
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
什么是上下文Context
Context 通过组件树提供了一个传递数据的方法从而避免了在每一个层级手动的传递 props 属性。
- 用法在父组件上定义getChildContext方法返回一个对象然后它的子组件就可以通过this.context属性来获取
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
class Header extends Component{
render() {
return (
<div>
<Title/>
</div>
)
}
}
class Title extends Component{
static contextTypes={
color:PropTypes.string
}
render() {
return (
<div style={{color:this.context.color}}>
Title
</div>
)
}
}
class Main extends Component{
render() {
return (
<div>
<Content>
</Content>
</div>
)
}
}
class Content extends Component{
static contextTypes={
color: PropTypes.string,
changeColor:PropTypes.func
}
render() {
return (
<div style={{color:this.context.color}}>
Content
<button onClick={()=>this.context.changeColor('green')}>绿色</button>
<button onClick={()=>this.context.changeColor('orange')}>橙色</button>
</div>
)
}
}
class Page extends Component{
constructor() {
super();
this.state={color:'red'};
}
static childContextTypes={
color: PropTypes.string,
changeColor:PropTypes.func
}
getChildContext() {
return {
color: this.state.color,
changeColor:(color)=>{
this.setState({color})
}
}
}
render() {
return (
<div>
<Header/>
<Main/>
</div>
)
}
}
ReactDOM.render(<Page/>,document.querySelector('#root'));
React-Router 4的Switch有什么用
Switch 通常被用来包裹 Route用于渲染与路径匹配的第一个子 <Route>
或 <Redirect>
它里面不能放其他元素。
假如不加 <Switch>
import { Route } from 'react-router-dom'
<Route path="/" component={Home}></Route>
<Route path="/login" component={Login}></Route>
Route 组件的 path 属性用于匹配路径因为需要匹配 /
到 Home
匹配 /login
到 Login
所以需要两个 Route但是不能这么写。这样写的话当 URL 的 path 为 “/login” 时<Route path="/" />
和<Route path="/login" />
都会被匹配因此页面会展示 Home 和 Login 两个组件。这时就需要借助 <Switch>
来做到只显示一个匹配组件
import { Switch, Route} from 'react-router-dom'
<Switch>
<Route path="/" component={Home}></Route>
<Route path="/login" component={Login}></Route>
</Switch>
此时再访问 “/login” 路径时却只显示了 Home 组件。这是就用到了exact属性它的作用就是精确匹配路径经常与<Switch>
联合使用。只有当 URL 和该 <Route>
的 path 属性完全一致的情况下才能匹配上
import { Switch, Route} from 'react-router-dom'
<Switch>
<Route exact path="/" component={Home}></Route>
<Route exact path="/login" component={Login}></Route>
</Switch>
React 中 keys 的作用是什么
Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。
在 React 中渲染集合时向每个重复的元素添加关键字对于帮助React跟踪元素与数据之间的关联非常重要。key 应该是唯一ID最好是 UUID 或收集项中的其他唯一字符串
<ul>
{todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
)};
</ul>
在集合中添加和删除项目时不使用键或将索引用作键会导致奇怪的行为。
为什么虚拟dom会提高性能
虚拟
dom
相当于在js
和真实dom
中间加了一个缓存利用dom diff
算法避免了没有必要的dom
操作从而提高性能
具体实现步骤如下
- 用
JavaScript
对象结构表示 DOM 树的结构然后用这个树构建一个真正的DOM
树插到文档当中 - 当状态变更的时候重新构造一棵新的对象树。然后用新的树和旧的树进行比较记录两棵树差异
- 把2所记录的差异应用到步骤1所构建的真正的
DOM
树上视图就更新
虚拟DOM一定会提高性能吗
很多人认为虚拟DOM一定会提高性能一定会更快其实这个说法有点片面因为虚拟DOM虽然会减少DOM操作但也无法避免DOM操作
- 它的优势是在于diff算法和批量处理策略,将所有的DOM操作搜集起来一次性去改变真实的DOM,但在首次渲染上虚拟DOM会多了一层计算消耗一些性能所以有可能会比html渲染的要慢
- 注意虚拟DOM实际上是给我们找了一条最短最近的路径并不是说比DOM操作的更快而是路径最简单
当渲染一个列表时何为 key设置 key 的目的是什么
Keys 会有助于 React 识别哪些 items
改变了被添加了或者被移除了。Keys 应该被赋予数组内的元素以赋予(DOM)元素一个稳定的标识选择一个 key 的最佳方法是使用一个字符串该字符串能惟一地标识一个列表项。很多时候你会使用数据中的 IDs 作为 keys当你没有稳定的 IDs 用于被渲染的 items
时可以使用项目索引作为渲染项的 key但这种方式并不推荐如果 items
可以重新排序就会导致 re-render
变慢。
React Hooks在平时开发中需要注意的问题和原因
1不要在循环条件或嵌套函数中调用Hook必须始终在 React函数的顶层使用Hook
这是因为React需要利用调用顺序来正确更新相应的状态以及调用相应的钩子函数。一旦在循环或条件分支语句中调用Hook就容易导致调用顺序的不一致性从而产生难以预料到的后果。
2使用useState时候使用pushpopsplice等直接更改数组对象的坑
使用push直接更改数组无法获取到新值应该采用析构方式但是在class里面不会有这个问题。代码示例
function Indicatorfilter() {
let [num,setNums] = useState([0,1,2,3])
const test = () => {
// 这里坑是直接采用push去更新num
// setNums(num)是无法更新num的
// 必须使用num = [...num ,1]
num.push(1)
// num = [...num ,1]
setNums(num)
}
return (
<div className='filter'>
<div onClick={test}>测试</div>
<div>
{num.map((item,index) => ( <div key={index}>{item}</div>
))} </div>
</div>
)
}
class Indicatorfilter extends React.Component<any,any>{
constructor(props:any){
super(props)
this.state = {
nums:[1,2,3]
}
this.test = this.test.bind(this)
}
test(){
// class采用同样的方式是没有问题的
this.state.nums.push(1)
this.setState({
nums: this.state.nums
})
}
render(){
let {nums} = this.state
return(
<div>
<div onClick={this.test}>测试</div>
<div>
{nums.map((item:any,index:number) => ( <div key={index}>{item}</div>
))} </div>
</div>
)
}
}
3useState设置状态的时候只有第一次生效后期需要更新状态必须通过useEffect
TableDeail是一个公共组件在调用它的父组件里面我们通过set改变columns的值以为传递给TableDeail 的 columns是最新的值所以tabColumn每次也是最新的值但是实际tabColumn是最开始的值不会随着columns的更新而更新
const TableDeail = ({ columns,}:TableData) => {
const [tabColumn, setTabColumn] = useState(columns)
}
// 正确的做法是通过useEffect改变这个值
const TableDeail = ({ columns,}:TableData) => {
const [tabColumn, setTabColumn] = useState(columns)
useEffect(() =>{setTabColumn(columns)},[columns])
}
4善用useCallback
父组件传递给子组件事件句柄时如果我们没有任何参数变动可能会选用useMemo。但是每一次父组件渲染子组件即使没变化也会跟着渲染一次。
5不要滥用useContext
可以使用基于 useContext 封装的状态管理工具。
参考 前端进阶面试题详细解答
可以使用TypeScript写React应用吗怎么操作
1如果还未创建 Create React App 项目
- 直接创建一个具有 typescript 的 Create React App 项目
npx create-react-app demo --typescript
2如果已经创建了 Create React App 项目需要将 typescript 引入到已有项目中
- 通过命令将 typescript 引入项目
npm install --save typescript @types/node @types/react @types/react-dom @types/jest
- 将项目中任何 后缀名为 ‘.js’ 的 JavaScript 文件重命名为 TypeScript 文件即后缀名为 ‘.tsx’例如 src/index.js 重命名为 src/index.tsx
React setState 调用的原理
具体的执行过程如下源码级解析
- 首先调用了
setState
入口函数入口函数在这里就是充当一个分发器的角色根据入参的不同将其分发到不同的功能函数中去
ReactComponent.prototype.setState = function (partialState, callback) {
this.updater.enqueueSetState(this, partialState);
if (callback) {
this.updater.enqueueCallback(this, callback, 'setState');
}
};
enqueueSetState
方法将新的state
放进组件的状态队列里并调用enqueueUpdate
来处理将要更新的实例对象
enqueueSetState: function (publicInstance, partialState) {
// 根据 this 拿到对应的组件实例
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
// 这个 queue 对应的就是一个组件实例的 state 数组
var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
queue.push(partialState);
// enqueueUpdate 用来处理当前的组件实例
enqueueUpdate(internalInstance);
}
- 在
enqueueUpdate
方法中引出了一个关键的对象——batchingStrategy
该对象所具备的isBatchingUpdates
属性直接决定了当下是要走更新流程还是应该排队等待如果轮到执行就调用batchedUpdates
方法来直接发起更新流程。由此可以推测batchingStrategy
或许正是 React 内部专门用于管控批量更新的对象。
function enqueueUpdate(component) {
ensureInjected();
// 注意这一句是问题的关键isBatchingUpdates标识着当前是否处于批量创建/更新组件的阶段
if (!batchingStrategy.isBatchingUpdates) {
// 若当前没有处于批量创建/更新组件的阶段则立即更新组件
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return;
}
// 否则先把组件塞入 dirtyComponents 队列里让它“再等等”
dirtyComponents.push(component);
if (component._updateBatchNumber == null) {
component._updateBatchNumber = updateBatchNumber + 1;
}
}
注意batchingStrategy
对象可以理解为“锁管理器”。这里的“锁”是指 React 全局唯一的 isBatchingUpdates
变量isBatchingUpdates
的初始值是 false
意味着“当前并未进行任何批量更新操作”。每当 React 调用 batchedUpdate
去执行更新动作时会先把这个锁给“锁上”置为 true
表明“现在正处于批量更新过程中”。当锁被“锁上”的时候任何需要更新的组件都只能暂时进入 dirtyComponents
里排队等候下一次的批量更新而不能随意“插队”。此处体现的“任务锁”的思想是 React 面对大量状态仍然能够实现有序分批处理的基石。
在React中组件的this.state和setState有什么区别
this.state通常是用来初始化state的this.setState是用来修改state值的。如果初始化了state之后再使用this.state之前的state会被覆盖掉如果使用this.setState只会替换掉相应的state值。所以如果想要修改state的值就需要使用setState而不能直接修改state直接修改state之后页面是不会更新的。
React中发起网络请求应该在哪个生命周期中进行为什么
对于异步请求最好放在componentDidMount中去操作对于同步的状态改变可以放在componentWillMount中一般用的比较少。
如果认为在componentWillMount里发起请求能提早获得结果这种想法其实是错误的通常componentWillMount比componentDidMount早不了多少微秒网络上任何一点延迟这一点差异都可忽略不计。
react的生命周期 constructor() -> componentWillMount() -> render() -> componentDidMount()
上面这些方法的调用是有次序的由上而下依次调用。
- constructor被调用是在组件准备要挂载的最开始此时组件尚未挂载到网页上。
- componentWillMount方法的调用在constructor之后在render之前在这方法里的代码调用setState方法不会触发重新render所以它一般不会用来作加载数据之用。
- componentDidMount方法中的代码是在组件已经完全挂载到网页上才会调用被执行所以可以保证数据的加载。此外在这方法中调用setState方法会触发重新渲染。所以官方设计这个方法就是用来加载外部数据用的或处理其他的副作用代码。与组件上的数据无关的加载也可以在constructor里做但constructor是做组件state初绐化工作并不是做加载数据这工作的constructor里也不能setState还有加载的时间太长或者出错页面就无法加载出来。所以有副作用的代码都会集中在componentDidMount方法里。
总结
- 跟服务器端渲染同构有关系如果在componentWillMount里面获取数据fetch data会执行两次一次在服务器端一次在客户端。在componentDidMount中可以解决这个问题componentWillMount同样也会render两次。
- 在componentWillMount中fetch data数据一定在render后才能到达如果忘记了设置初始状态用户体验不好。
- react16.0以后componentWillMount可能会被执行多次。
constructor 为什么不先渲染
由ES6的继承规则得知不管子类写不写constructor在new实例的过程都会给补上constructor。
所以constructor钩子函数并不是不可缺少的子组件可以在一些情况略去。比如不自己的state从props中获取的情况
类组件和函数组件有何不同
解答
在 React 16.8版本引入钩子之前使用基于类的组件来创建需要维护内部状态或利用生命周期方法的组件即componentDidMount
和shouldComponentUpdate
。基于类的组件是 ES6 类它扩展了 React 的 Component 类并且至少实现了render()
方法。
类组件
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
函数组件是无状态的同样小于 React 16.8版本并返回要呈现的输出。它们渲染 UI 的首选只依赖于属性因为它们比基于类的组件更简单、更具性能。
函数组件
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
注意在 React 16.8版本中引入钩子意味着这些区别不再适用请参阅14和15题。
setState 是同步异步为什么实现原理
1. setState是同步执行的
setState是同步执行的但是state并不一定会同步更新
2. setState在React生命周期和合成事件中批量覆盖执行
在React的生命周期钩子和合成事件中多次执行setState会批量执行
具体表现为多次同步执行的setState会进行合并类似于Object.assign相同的key后面的会覆盖前面的
当遇到多个setState调用时候会提取单次传递setState的对象把他们合并在一起形成一个新的
单一对象并用这个单一的对象去做setState的事情就像Object.assign的对象合并后一个
key值会覆盖前面的key值
经过React 处理的事件是不会同步更新 this.state的. 通过 addEventListener || setTimeout/setInterval 的方式处理的则会同步更新。
为了合并setState我们需要一个队列来保存每次setState的数据然后在一段时间后执行合并操作和更新state并清空这个队列然后渲染组件。
React 高阶组件是什么和普通组件有什么区别适用什么场景
官方解释∶
高阶组件HOC是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分它是一种基于 React 的组合特性而形成的设计模式。
高阶组件HOC就是一个函数且该函数接受一个组件作为参数并返回一个新的组件它只是一种组件的设计模式这种设计模式是由react自身的组合性质必然产生的。我们将它们称为纯组件因为它们可以接受任何动态提供的子组件但它们不会修改或复制其输入组件中的任何行为。
// hoc的定义
function withSubscription(WrappedComponent, selectData) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
data: selectData(DataSource, props)
};
}
// 一些通用的逻辑处理
render() {
// ... 并使用新数据渲染被包装的组件!
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
// 使用
const BlogPostWithSubscription = withSubscription(BlogPost,
(DataSource, props) => DataSource.getBlogPost(props.id));
1HOC的优缺点
- 优点∶ 逻辑服用、不影响被包裹组件的内部逻辑。
- 缺点∶hoc传递给被包裹组件的props容易和被包裹后的组件重名进而被覆盖
2适用场景
- 代码复用逻辑抽象
- 渲染劫持
- State 抽象和更改
- Props 更改
3具体应用例子
- 权限控制 利用高阶组件的 条件渲染 特性可以对页面进行权限控制权限控制一般分为两个维度页面级别和 页面元素级别
// HOC.js
function withAdminAuth(WrappedComponent) {
return class extends React.Component {
state = {
isAdmin: false,
}
async UNSAFE_componentWillMount() {
const currentRole = await getCurrentUserRole();
this.setState({
isAdmin: currentRole === 'Admin',
});
}
render() {
if (this.state.isAdmin) {
return <WrappedComponent {...this.props} />;
} else {
return (<div>您没有权限查看该页面请联系管理员</div>);
}
}
};
}
// pages/page-a.js
class PageA extends React.Component {
constructor(props) {
super(props);
// something here...
}
UNSAFE_componentWillMount() {
// fetching data
}
render() {
// render page with data
}
}
export default withAdminAuth(PageA);
// pages/page-b.js
class PageB extends React.Component {
constructor(props) {
super(props);
// something here...
}
UNSAFE_componentWillMount() {
// fetching data
}
render() {
// render page with data
}
}
export default withAdminAuth(PageB);
- 组件渲染性能追踪 借助父组件子组件生命周期规则捕获子组件的生命周期可以方便的对某个组件的渲染时间进行记录∶
class Home extends React.Component {
render() {
return (<h1>Hello World.</h1>);
}
}
function withTiming(WrappedComponent) {
return class extends WrappedComponent {
constructor(props) {
super(props);
this.start = 0;
this.end = 0;
}
UNSAFE_componentWillMount() {
super.componentWillMount && super.componentWillMount();
this.start = Date.now();
}
componentDidMount() {
super.componentDidMount && super.componentDidMount();
this.end = Date.now();
console.log(`${WrappedComponent.name} 组件渲染时间为 ${this.end - this.start} ms`);
}
render() {
return super.render();
}
};
}
export default withTiming(Home);
注意withTiming 是利用 反向继承 实现的一个高阶组件功能是计算被包裹组件这里是 Home 组件的渲染时间。
- 页面复用
const withFetching = fetching => WrappedComponent => {
return class extends React.Component {
state = {
data: [],
}
async UNSAFE_componentWillMount() {
const data = await fetching();
this.setState({
data,
});
}
render() {
return <WrappedComponent data={this.state.data} {...this.props} />;
}
}
}
// pages/page-a.js
export default withFetching(fetching('science-fiction'))(MovieList);
// pages/page-b.js
export default withFetching(fetching('action'))(MovieList);
// pages/page-other.js
export default withFetching(fetching('some-other-type'))(MovieList);
React Hooks 和生命周期的关系
函数组件 的本质是函数没有 state 的概念的因此不存在生命周期一说仅仅是一个 render 函数而已。
但是引入 Hooks 之后就变得不同了它能让组件在不使用 class 的情况下拥有 state所以就有了生命周期的概念所谓的生命周期其实就是 useState
、 useEffect()
和 useLayoutEffect()
。
即Hooks 组件使用了Hooks的函数组件有生命周期而函数组件未使用Hooks的函数组件是没有生命周期的。
下面是具体的 class 与 Hooks 的生命周期对应关系
constructor
函数组件不需要构造函数可以通过调用**useState 来初始化 state**
。如果计算的代价比较昂贵也可以传一个函数给useState
。
const [num, UpdateNum] = useState(0)
getDerivedStateFromProps
一般情况下我们不需要使用它可以在渲染过程中更新 state以达到实现getDerivedStateFromProps
的目的。
function ScrollView({row}) {
let [isScrollingDown, setIsScrollingDown] = useState(false);
let [prevRow, setPrevRow] = useState(null);
if (row !== prevRow) {
// Row 自上次渲染以来发生过改变。更新 isScrollingDown。
setIsScrollingDown(prevRow !== null && row > prevRow);
setPrevRow(row);
}
return `Scrolling down: ${isScrollingDown}`;
}
React 会立即退出第一次渲染并用更新后的 state 重新运行组件以避免耗费太多性能。
shouldComponentUpdate
可以用**React.memo**
包裹一个组件来对它的props
进行浅比较
const Button = React.memo((props) => { // 具体的组件});
注意**React.memo 等效于 **``**PureComponent**
它只浅比较 props。这里也可以使用 useMemo
优化每一个节点。
render
这是函数组件体本身。componentDidMount
,componentDidUpdate
useLayoutEffect
与它们两的调用阶段是一样的。但是我们推荐你一开始先用 useEffect只有当它出问题的时候再尝试使用useLayoutEffect
。useEffect
可以表达所有这些的组合。
// componentDidMount
useEffect(()=>{
// 需要在 componentDidMount 执行的内容
}, [])
useEffect(() => {
// 在 componentDidMount以及 count 更改时 componentDidUpdate 执行的内容
document.title = `You clicked ${count} times`;
return () => {
// 需要在 count 更改时 componentDidUpdate先于 document.title = ... 执行遵守先清理后更新
// 以及 componentWillUnmount 执行的内容
} // 当函数中 Cleanup 函数会按照在代码中定义的顺序先后执行与函数本身的特性无关
}, [count]); // 仅在 count 更改时更新
请记得 React 会等待浏览器完成画面渲染之后才会延迟调用 因此会使得额外操作很方便
componentWillUnmount
相当于useEffect
里面返回的cleanup
函数
// componentDidMount/componentWillUnmount
useEffect(()=>{
// 需要在 componentDidMount 执行的内容
return function cleanup() {
// 需要在 componentWillUnmount 执行的内容
}
}, [])
componentDidCatch
andgetDerivedStateFromError
目前还没有这些方法的 Hook 等价写法但很快会加上。
class 组件 | Hooks 组件 |
---|---|
constructor | useState |
getDerivedStateFromProps | useState 里面 update 函数 |
shouldComponentUpdate | useMemo |
render | 函数本身 |
componentDidMount | useEffect |
componentDidUpdate | useEffect |
componentWillUnmount | useEffect 里面返回的函数 |
componentDidCatch | 无 |
getDerivedStateFromError | 无 |
React 的工作原理
React 会创建一个虚拟 DOM(virtual DOM)。当一个组件中的状态改变时React 首先会通过 “diffing” 算法来标记虚拟 DOM 中的改变第二步是调节(reconciliation)会用 diff 的结果来更新 DOM。
React中的props为什么是只读的
this.props
是组件之间沟通的一个接口原则上来讲它只能从父组件流向子组件。React具有浓重的函数式编程的思想。
提到函数式编程就要提一个概念纯函数。它有几个特点
- 给定相同的输入总是返回相同的输出。
- 过程没有副作用。
- 不依赖外部状态。
this.props
就是汲取了纯函数的思想。props的不可以变性就保证的相同的输入页面显示的内容是一样的并且不会产生副作用
React 事件机制
<div onClick={this.handleClick.bind(this)}>点我</div>
React并不是将click事件绑定到了div的真实DOM上而是在document处监听了所有的事件当事件发生并且冒泡到document处的时候React将事件内容封装并交由真正的处理函数运行。这样的方式不仅仅减少了内存的消耗还能在组件挂在销毁时统一订阅和移除事件。
除此之外冒泡到document上的事件也不是原生的浏览器事件而是由react自己实现的合成事件SyntheticEvent。因此如果不想要是事件冒泡的话应该调用event.preventDefault()方法而不是调用event.stopProppagation()方法。 JSX 上写的事件并没有绑定在对应的真实 DOM 上而是通过事件代理的方式将所有的事件都统一绑定在了 document
上。这样的方式不仅减少了内存消耗还能在组件挂载销毁时统一订阅和移除事件。
另外冒泡到 document
上的事件也不是原生浏览器事件而是 React 自己实现的合成事件SyntheticEvent。因此我们如果不想要事件冒泡的话调用 event.stopPropagation
是无效的而应该调用 event.preventDefault
。
实现合成事件的目的如下
- 合成事件首先抹平了浏览器之间的兼容问题另外这是一个跨浏览器原生事件包装器赋予了跨浏览器开发的能力
- 对于原生浏览器事件来说浏览器会给监听器创建一个事件对象。如果你有很多的事件监听那么就需要分配很多的事件对象造成高额的内存分配问题。但是对于合成事件来说有一个事件池专门来管理它们的创建和销毁当事件需要被使用时就会从池子中复用对象事件回调结束后就会销毁事件对象上的属性从而便于下次复用事件对象。
hooks父子传值
父传子
在父组件中用useState声明数据
const [ data, setData ] = useState(false)
把数据传递给子组件
<Child data={data} />
子组件接收
export default function (props) {
const { data } = props
console.log(data)
}
子传父
子传父可以通过事件方法传值和父传子有点类似。
在父组件中用useState声明数据
const [ data, setData ] = useState(false)
把更新数据的函数传递给子组件
<Child setData={setData} />
子组件中触发函数更新数据就会直接传递给父组件
export default function (props) {
const { setData } = props
setData(true)
}
如果存在多个层级的数据传递也可依照此方法依次传递
// 多层级用useContext
const User = () => {
// 直接获取不用回调
const { user, setUser } = useContext(UserContext);
return <Avatar user={user} setUser={setUser} />;
};
高阶组件存在的问题
- 静态方法丢失(必须将静态方法做拷贝)
refs
属性不能透传(如果你向一个由高阶组件创建的组件的元素添加ref
引用那么ref
指向的是最外层容器组件实例的而不是被包裹的WrappedComponent
组件。)- 反向继承不能保证完整的子组件树被解析
React 组件有两种形式分别是 class 类型和 function 类型无状态组件。
我们知道反向继承的渲染劫持可以控制 WrappedComponent
的渲染过程也就是说这个过程中我们可以对 elements tree
、 state
、 props
或 render()
的结果做各种操作。
但是如果渲染 elements tree
中包含了 function 类型的组件的话这时候就不能操作组件的子组件了。