关于前端:百度前端高频react面试题持续更新中

3次阅读

共计 14605 个字符,预计需要花费 37 分钟才能阅读完成。

说说你用 react 有什么坑点?

1. JSX 做表达式判断时候,须要强转为 boolean 类型

如果不应用 !!b 进行强转数据类型,会在页面外面输入 0

render() {
  const b = 0;
  return <div>
    {!!b && <div> 这是一段文本 </div>}
  </div>
}

2. 尽量不要在 componentWillReviceProps 里应用 setState,如果肯定要应用,那么须要判断完结条件,不然会呈现有限重渲染,导致页面解体

3. 给组件增加 ref 时候,尽量不要应用匿名函数,因为当组件更新的时候,匿名函数会被当做新的 prop 解决,让 ref 属性承受到新函数的时候,react 外部会先清空 ref,也就是会以 null 为回调参数先执行一次 ref 这个 props,而后在以该组件的实例执行一次 ref,所以用匿名函数做 ref 的时候,有的时候去 ref 赋值后的属性会取到 null

4. 遍历子节点的时候,不要用 index 作为组件的 key 进行传入

React 中的 props 为什么是只读的?

this.props是组件之间沟通的一个接口,原则上来讲,它只能从父组件流向子组件。React 具备浓厚的函数式编程的思维。

提到函数式编程就要提一个概念:纯函数。它有几个特点:

  • 给定雷同的输出,总是返回雷同的输入。
  • 过程没有副作用。
  • 不依赖内部状态。

this.props就是吸取了纯函数的思维。props 的不能够变性就保障的雷同的输出,页面显示的内容是一样的,并且不会产生副作用

高阶组件存在的问题

  • 静态方法失落(必须将静态方法做拷贝)
  • refs 属性不能透传 (如果你向一个由高阶组件创立的组件的元素增加ref 援用,那么 ref 指向的是最外层容器组件实例的,而不是被包裹的 WrappedComponent 组件。)
  • 反向继承不能保障残缺的子组件树被解析
    React 组件有两种模式,别离是 class 类型和 function 类型(无状态组件)。

咱们晓得反向继承的渲染劫持能够管制 WrappedComponent 的渲染过程,也就是说这个过程中咱们能够对 elements treestatepropsrender() 的后果做各种操作。

然而如果渲染 elements tree 中蕴含了 function 类型的组件的话,这时候就不能操作组件的子组件了。

对有状态组件和无状态组件的了解及应用场景

(1)有状态组件

特点:

  • 是类组件
  • 有继承
  • 能够应用 this
  • 能够应用 react 的生命周期
  • 应用较多,容易频繁触发生命周期钩子函数,影响性能
  • 外部应用 state,保护本身状态的变动,有状态组件依据内部组件传入的 props 和本身的 state 进行渲染。

应用场景:

  • 须要应用到状态的。
  • 须要应用状态操作组件的(无状态组件的也能够实现新版本 react hooks 也可实现)

总结: 类组件能够保护本身的状态变量,即组件的 state,类组件还有不同的生命周期办法,能够让开发者可能在组件的不同阶段(挂载、更新、卸载),对组件做更多的管制。类组件则既能够充当无状态组件,也能够充当有状态组件。当一个类组件不须要治理本身状态时,也可称为无状态组件。

(2)无状态组件 特点:

  • 不依赖本身的状态 state
  • 能够是类组件或者函数组件。
  • 能够完全避免应用 this 关键字。(因为应用的是箭头函数事件无需绑定)
  • 有更高的性能。当不须要应用生命周期钩子时,应该首先应用无状态函数组件
  • 组件外部不保护 state,只依据内部组件传入的 props 进行渲染的组件,当 props 扭转时,组件从新渲染。

应用场景:

  • 组件不须要治理 state,纯展现

长处:

  • 简化代码、专一于 render
  • 组件不须要被实例化,无生命周期,晋升性能。输入(渲染)只取决于输出(属性),无副作用
  • 视图和数据的解耦拆散

毛病:

  • 无奈应用 ref
  • 无生命周期办法
  • 无法控制组件的重渲染,因为无奈应用 shouldComponentUpdate 办法,当组件承受到新的属性时则会重渲染

总结: 组件外部状态且与内部无关的组件,能够思考用状态组件,这样状态树就不会过于简单,易于了解和治理。当一个组件不须要治理本身状态时,也就是无状态组件,应该优先设计为函数组件。比方自定义的 <Button/><Input /> 等组件。

什么是受控组件和非受控组件

  • 受状态管制的组件,必须要有 onChange 办法,否则不能应用 受控组件能够赋予默认值(官网举荐应用 受控组件)实现双向数据绑定
class Input extends Component{constructor(){super();
        this.state = {val:'100'}
    }
    handleChange = (e) =>{ // e 是事件源
        let val = e.target.value;
        this.setState({val});
    };
    render(){
        return (<div>
            <input type="text" value={this.state.val} onChange={this.handleChange}/>
            {this.state.val}
        </div>)
    }
}
  • 非受控也就意味着我能够不须要设置它的 state 属性,而通过 ref 来操作实在的 DOM
class Sum extends Component{constructor(){super();
        this.state =  {result:''}
    }
    // 通过 ref 设置的属性 能够通过 this.refs 获取到对应的 dom 元素
    handleChange = () =>{
        let result = this.refs.a.value + this.b.value;
        this.setState({result});
    };
    render(){
        return (<div onChange={this.handleChange}>
                <input type="number" ref="a"/>
                {/* x 代表的实在的 dom, 把元素挂载在了以后实例上 */}
                <input type="number" ref={(x)=>{this.b = x;}}/>
                {this.state.result}
            </div>
        )
    }
}

React 16 中新生命周期有哪些

对于 React16 开始利用的新生命周期:能够看出,React16 自上而下地对生命周期做了另一种维度的解读:

  • Render 阶段:用于计算一些必要的状态信息。这个阶段可能会被 React 暂停,这一点和 React16 引入的 Fiber 架构(咱们前面会重点解说)是无关的;
  • Pre-commit 阶段:所谓“commit”,这里指的是“更新真正的 DOM 节点”这个动作。所谓 Pre-commit,就是说我在这个阶段其实还并没有去更新实在的 DOM,不过 DOM 信息曾经是能够读取的了;
  • Commit 阶段:在这一步,React 会实现实在 DOM 的更新工作。Commit 阶段,咱们能够拿到实在 DOM(包含 refs)。

与此同时,新的生命周期在流程方面,依然遵循“挂载”、“更新”、“卸载”这三个狭义的划分形式。它们别离对应到:

  • 挂载过程:

    • constructor
    • getDerivedStateFromProps
    • render
    • componentDidMount
  • 更新过程:

    • getDerivedStateFromProps
    • shouldComponentUpdate
    • render
    • getSnapshotBeforeUpdate
    • componentDidUpdate
  • 卸载过程:

    • componentWillUnmount

参考 前端进阶面试题具体解答

React 中什么是受控组件和非控组件?

(1)受控组件 在应用表单来收集用户输出时,例如<input><select><textearea> 等元素都要绑定一个 change 事件,当表单的状态发生变化,就会触发 onChange 事件,更新组件的 state。这种组件在 React 中被称为 受控组件,在受控组件中,组件渲染出的状态与它的 value 或 checked 属性绝对应,react 通过这种形式打消了组件的部分状态,使整个状态可控。react 官网举荐应用受控表单组件。

受控组件更新 state 的流程:

  • 能够通过初始 state 中设置表单的默认值
  • 每当表单的值发生变化时,调用 onChange 事件处理器
  • 事件处理器通过事件对象 e 拿到扭转后的状态,并更新组件的 state
  • 一旦通过 setState 办法更新 state,就会触发视图的从新渲染,实现表单组件的更新

受控组件缺点: 表单元素的值都是由 React 组件进行治理,当有多个输入框,或者多个这种组件时,如果想同时获取到全副的值就必须每个都要编写事件处理函数,这会让代码看着很臃肿,所以为了解决这种状况,呈现了非受控组件。

(2)非受控组件 如果一个表单组件没有 value props(单选和复选按钮对应的是 checked props)时,就能够称为非受控组件。在非受控组件中,能够应用一个 ref 来从 DOM 取得表单值。而不是为每个状态更新编写一个事件处理程序。

React 官网的解释:

要编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,你能够应用 ref 来从 DOM 节点中获取表单数据。
因为非受控组件将实在数据贮存在 DOM 节点中,所以在应用非受控组件时,有时候反而更容易同时集成 React 和非 React 代码。如果你不介意代码好看性,并且心愿疾速编写代码,应用非受控组件往往能够缩小你的代码量。否则,你应该应用受控组件。

例如,上面的代码在非受控组件中接管单个属性:

class NameForm extends React.Component {constructor(props) {super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
  handleSubmit(event) {alert('A name was submitted:' + this.input.value);
    event.preventDefault();}
  render() {
    return (<form onSubmit={this.handleSubmit}>
        <label>
          Name:          <input type="text" ref={(input) => this.input = input} />        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

总结: 页面中所有输出类的 DOM 如果是现用现取的称为非受控组件,而通过 setState 将输出的值保护到了 state 中,须要时再从 state 中取出,这里的数据就受到了 state 的管制,称为受控组件。

Redux 中异步的申请怎么解决

能够在 componentDidmount 中间接进⾏申请⽆须借助 redux。然而在⼀定规模的项⽬中, 上述⽅法很难进⾏异步流的治理, 通常状况下咱们会借助 redux 的异步中间件进⾏异步解决。redux 异步流中间件其实有很多,当下支流的异步中间件有两种 redux-thunk、redux-saga。

(1)应用 react-thunk 中间件

redux-thunk长处:

  • 体积⼩: redux-thunk 的实现⽅式很简略, 只有不到 20 ⾏代码
  • 使⽤简略: redux-thunk 没有引⼊像 redux-saga 或者 redux-observable 额定的范式, 上⼿简略

redux-thunk缺点:

  • 样板代码过多: 与 redux 自身⼀样, 通常⼀个申请须要⼤量的代码, ⽽且很多都是反复性质的
  • 耦合重大: 异步操作与 redux 的 action 偶合在⼀起, 不⽅便治理
  • 性能孱弱: 有⼀些理论开发中常⽤的性能须要⾃⼰进⾏封装

应用步骤:

  • 配置中间件,在 store 的创立中配置
import {createStore, applyMiddleware, compose} from 'redux';
import reducer from './reducer';
import thunk from 'redux-thunk'

// 设置调试工具
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
// 设置中间件
const enhancer = composeEnhancers(applyMiddleware(thunk)
);

const store = createStore(reducer, enhancer);

export default store;
  • 增加一个返回函数的 actionCreator,将异步申请逻辑放在外面
/**  发送 get 申请,并生成相应 action,更新 store 的函数  @param url {string} 申请地址  @param func {function} 真正须要生成的 action 对应的 actionCreator  @return {function} */
// dispatch 为主动接管的 store.dispatch 函数 
export const getHttpAction = (url, func) => (dispatch) => {axios.get(url).then(function(res){const action = func(res.data)
        dispatch(action)
    })
}
  • 生成 action,并发送 action
componentDidMount(){var action = getHttpAction('/getData', getInitTodoItemAction)
    // 发送函数类型的 action 时,该 action 的函数领会主动执行
    store.dispatch(action)
}

(2)应用 redux-saga 中间件

redux-saga长处:

  • 异步解耦: 异步操作被被转移到独自 saga.js 中,不再是掺杂在 action.js 或 component.js 中
  • action 解脱 thunk function: dispatch 的参数仍然是⼀个纯正的 action (FSA),⽽不是充斥“⿊魔法”thunk function
  • 异样解决: 受害于 generator function 的 saga 实现,代码异样 / 申请失败 都能够间接通过 try/catch 语法间接捕捉解决
  • 性能强⼤: redux-saga 提供了⼤量的 Saga 辅助函数和 Effect 创立器供开发者使⽤, 开发者⽆须封装或者简略封装即可使⽤
  • 灵便: redux-saga 能够将多个 Saga 能够串⾏ / 并⾏组合起来, 造成⼀个⾮常实⽤的异步 flow
  • 易测试,提供了各种 case 的测试⽅案,包含 mock task,分⽀笼罩等等

redux-saga缺点:

  • 额定的学习老本: redux-saga 不仅在使⽤难以了解的 generator function, ⽽且无数⼗个 API, 学习老本远超 redux-thunk, 最重要的是你的额定学习老本是只服务于这个库的, 与 redux-observable 不同,redux-observable 尽管也有额定学习老本然而背地是 rxjs 和⼀整套思维
  • 体积庞⼤: 体积略⼤, 代码近 2000 ⾏,min 版 25KB 左右
  • 性能过剩: 实际上并发管制等性能很难⽤到, 然而咱们仍然须要引⼊这些代码
  • ts ⽀持不敌对: yield ⽆法返回 TS 类型

redux-saga 能够捕捉 action,而后执行一个函数,那么能够把异步代码放在这个函数中,应用步骤如下:

  • 配置中间件
import {createStore, applyMiddleware, compose} from 'redux';
import reducer from './reducer';
import createSagaMiddleware from 'redux-saga'
import TodoListSaga from './sagas'

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const sagaMiddleware = createSagaMiddleware()

const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware)
);

const store = createStore(reducer, enhancer);
sagaMiddleware.run(TodoListSaga)

export default store;
  • 将异步申请放在 sagas.js 中
import {takeEvery, put} from 'redux-saga/effects'
import {initTodoList} from './actionCreator'
import {GET_INIT_ITEM} from './actionTypes'
import axios from 'axios'

function* func(){
    try{
        // 能够获取异步返回数据
        const res = yield axios.get('/getData')
        const action = initTodoList(res.data)
        // 将 action 发送到 reducer
        yield put(action)
    }catch(e){console.log('网络申请失败')
    }
}

function* mySaga(){
    // 主动捕捉 GET_INIT_ITEM 类型的 action,并执行 func
    yield takeEvery(GET_INIT_ITEM, func)
}

export default mySaga
  • 发送 action
componentDidMount(){const action = getInitTodoItemAction()
  store.dispatch(action)
}

对 React-Intl 的了解,它的工作原理?

React-intl 是雅虎的语言国际化开源我的项目 FormatJS 的一部分,通过其提供的组件和 API 能够与 ReactJS 绑定。

React-intl 提供了两种应用办法,一种是援用 React 组件,另一种是间接调取 API,官网更加举荐在 React 我的项目中应用前者,只有在无奈应用 React 组件的中央,才应该调用框架提供的 API。它提供了一系列的 React 组件,包含数字格式化、字符串格式化、日期格式化等。

在 React-intl 中,能够配置不同的语言包,他的工作原理就是依据须要,在语言包之间进行切换。

对 React context 的了解

在 React 中,数据传递个别应用 props 传递数据,维持单向数据流,这样能够让组件之间的关系变得简略且可预测,然而单项数据流在某些场景中并不实用。单纯一对的父子组件传递并无问题,但要是组件之间层层依赖深刻,props 就须要层层传递显然,这样做太繁琐了。

Context 提供了一种在组件之间共享此类值的形式,而不用显式地通过组件树的逐层传递 props。

能够把 context 当做是特定一个组件树内共享的 store,用来做数据传递。简略说就是,当你不想在组件树中通过逐层传递 props 或者 state 的形式来传递数据时,能够应用 Context 来实现跨层级的组件数据传递。

JS 的代码块在执行期间,会创立一个相应的作用域链,这个作用域链记录着运行时 JS 代码块执行期间所能拜访的流动对象,包含变量和函数,JS 程序通过作用域链拜访到代码块外部或者内部的变量和函数。

如果以 JS 的作用域链作为类比,React 组件提供的 Context 对象其实就好比一个提供给子组件拜访的作用域,而 Context 对象的属性能够看成作用域上的流动对象。因为组件 的 Context 由其父节点链上所有组件通 过 getChildContext()返回的 Context 对象组合而成,所以,组件通过 Context 是能够拜访到其父组件链上所有节点组件提供的 Context 的属性。

为什么 React 并不举荐优先思考应用 Context?

  • Context 目前还处于试验阶段,可能会在前面的发行版本中有很大的变动,事实上这种状况曾经产生了,所以为了防止给今后降级带来大的影响和麻烦,不倡议在 app 中应用 context。
  • 只管不倡议在 app 中应用 context,然而独有组件而言,因为影响范畴小于 app,如果能够做到高内聚,不毁坏组件树之间的依赖关系,能够思考应用 context
  • 对于组件之间的数据通信或者状态治理,无效应用 props 或者 state 解决,而后再思考应用第三方的成熟库进行解决,以上的办法都不是最佳的计划的时候,在思考 context。
  • context 的更新须要通过 setState()触发,然而这并不是很牢靠的,Context 反对跨组件的拜访,然而如果两头的子组件通过一些办法不影响更新,比方 shouldComponentUpdate() 返回 false 那么不能保障 Context 的更新肯定能够应用 Context 的子组件,因而,Context 的可靠性须要关注

React 中 refs 的作用是什么?有哪些利用场景?

Refs 提供了一种形式,用于拜访在 render 办法中创立的 React 元素或 DOM 节点。Refs 应该审慎应用,如下场景应用 Refs 比拟适宜:

  • 解决焦点、文本抉择或者媒体的管制
  • 触发必要的动画
  • 集成第三方 DOM 库

Refs 是应用 React.createRef() 办法创立的,他通过 ref 属性附加到 React 元素上。要在整个组件中应用 Refs,须要将 ref 在构造函数中调配给其实例属性:

class MyComponent extends React.Component {constructor(props) {super(props)
    this.myRef = React.createRef()}
  render() {return <div ref={this.myRef} />
  }
}

因为函数组件没有实例,因而不能在函数组件上间接应用 ref

function MyFunctionalComponent() {return <input />;}
class Parent extends React.Component {constructor(props) {super(props);
    this.textInput = React.createRef();}
  render() {
    // 这将不会工作!return (<MyFunctionalComponent ref={this.textInput} />
    );
  }
}

但能够通过闭合的帮忙在函数组件外部进行应用 Refs:

function CustomTextInput(props) {
  // 这里必须申明 textInput,这样 ref 回调才能够援用它
  let textInput = null;
  function handleClick() {textInput.focus();
  }
  return (
    <div>
      <input
        type="text"
        ref={(input) => {textInput = input;}} />      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );  
}

留神:

  • 不应该适度的应用 Refs
  • ref 的返回值取决于节点的类型:

    • ref 属性被用于一个一般的 HTML 元素时,React.createRef() 将接管底层 DOM 元素作为他的 current 属性以创立 ref
    • ref 属性被用于一个自定义的类组件时,ref 对象将接管该组件已挂载的实例作为他的 current
  • 当在父组件中须要拜访子组件中的 ref 时可应用传递 Refs 或回调 Refs。

React 申明组件有哪几种办法,有什么不同?

React 申明组件的三种形式:

  • 函数式定义的 无状态组件
  • ES5 原生形式 React.createClass 定义的组件
  • ES6 模式的 extends React.Component 定义的组件

(1)无状态函数式组件 它是为了创立纯展现组件,这种组件只负责依据传入的 props 来展现,不波及到 state 状态的操作
组件不会被实例化,整体渲染性能失去晋升,不能拜访 this 对象,不能拜访生命周期的办法

(2)ES5 原生形式 React.createClass // RFC React.createClass 会自绑定函数办法,导致不必要的性能开销,减少代码过期的可能性。

(3)E6 继承模式 React.Component // RCC 目前极为举荐的创立有状态组件的形式,最终会取代 React.createClass 模式;绝对于 React.createClass 能够更好实现代码复用。

无状态组件绝对于于后者的区别: 与无状态组件相比,React.createClass 和 React.Component 都是创立有状态的组件,这些组件是要被实例化的,并且能够拜访组件的生命周期办法。

React.createClass 与 React.Component 区别:

① 函数 this 自绑定

  • React.createClass 创立的组件,其每一个成员函数的 this 都有 React 主动绑定,函数中的 this 会被正确设置。
  • React.Component 创立的组件,其成员函数不会主动绑定 this,须要开发者手动绑定,否则 this 不能获取以后组件实例对象。

② 组件属性类型 propTypes 及其默认 props 属性 defaultProps 配置不同

  • React.createClass 在创立组件时,无关组件 props 的属性类型及组件默认的属性会作为组件实例的属性来配置,其中 defaultProps 是应用 getDefaultProps 的办法来获取默认组件属性的
  • React.Component 在创立组件时配置这两个对应信息时,他们是作为组件类的属性,不是组件实例的属性,也就是所谓的类的动态属性来配置的。

③ 组件初始状态 state 的配置不同

  • React.createClass 创立的组件,其状态 state 是通过 getInitialState 办法来配置组件相干的状态;
  • React.Component 创立的组件,其状态 state 是在 constructor 中像初始化组件属性一样申明的。

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 可能会被执行屡次。

在 React 中组件的 props 扭转时更新组件的有哪些办法?

在一个组件传入的 props 更新时从新渲染该组件罕用的办法是在 componentWillReceiveProps 中将新的 props 更新到组件的 state 中(这种 state 被成为派生状态(Derived State)),从而实现从新渲染。React 16.3 中还引入了一个新的钩子函数 getDerivedStateFromProps 来专门实现这一需要。

(1)componentWillReceiveProps(已废除)

在 react 的 componentWillReceiveProps(nextProps)生命周期中,能够在子组件的 render 函数执行前,通过 this.props 获取旧的属性,通过 nextProps 获取新的 props,比照两次 props 是否雷同,从而更新子组件本人的 state。

这样的益处是,能够将数据申请放在这里进行执行,须要传的参数则从 componentWillReceiveProps(nextProps)中获取。而不用将所有的申请都放在父组件中。于是该申请只会在该组件渲染时才会收回,从而加重申请累赘。

(2)getDerivedStateFromProps(16.3 引入)

这个生命周期函数是为了代替 componentWillReceiveProps 存在的,所以在须要应用 componentWillReceiveProps 时,就能够思考应用 getDerivedStateFromProps 来进行代替。

两者的参数是不雷同的,而 getDerivedStateFromProps 是一个动态函数,也就是这个函数不能通过 this 拜访到 class 的属性,也并不举荐间接拜访属性。而是应该通过参数提供的 nextProps 以及 prevState 来进行判断,依据新传入的 props 来映射到 state。

须要留神的是,如果 props 传入的内容不须要影响到你的 state,那么就须要返回一个 null,这个返回值是必须的,所以尽量将其写到函数的开端:

static getDerivedStateFromProps(nextProps, prevState) {const {type} = nextProps;
    // 当传入的 type 发生变化的时候,更新 state
    if (type !== prevState.type) {
        return {type,};
    }
    // 否则,对于 state 不进行任何操作
    return null;
}

解释 React 中 render() 的目标。

每个 React 组件强制要求必须有一个 render()。它返回一个 React 元素,是原生 DOM 组件的示意。如果须要渲染多个 HTML 元素,则必须将它们组合在一个关闭标记内,例如 <form><group><div> 等。此函数必须放弃污浊,即必须每次调用时都返回雷同的后果。

哪些办法会触发 React 从新渲染?从新渲染 render 会做些什么?

(1)哪些办法会触发 react 从新渲染?

  • setState()办法被调用

setState 是 React 中最罕用的命令,通常状况下,执行 setState 会触发 render。然而这里有个点值得关注,执行 setState 的时候不肯定会从新渲染。当 setState 传入 null 时,并不会触发 render。

class App extends React.Component {
  state = {a: 1};

  render() {console.log("render");
    return (
      <React.Fragement>
        <p>{this.state.a}</p>
        <button
          onClick={() => {            this.setState({ a: 1}); // 这里并没有扭转 a 的值          }}        >          Click me        </button>
        <button onClick={() => this.setState(null)}>setState null</button>
        <Child />
      </React.Fragement>
    );
  }
}
  • 父组件从新渲染

只有父组件从新渲染了,即便传入子组件的 props 未发生变化,那么子组件也会从新渲染,进而触发 render

(2)从新渲染 render 会做些什么?

  • 会对新旧 VNode 进行比照,也就是咱们所说的 Diff 算法。
  • 对新旧两棵树进行一个深度优先遍历,这样每一个节点都会一个标记,在到深度遍历的时候,每遍历到一和个节点,就把该节点和新的节点树进行比照,如果有差别就放到一个对象外面
  • 遍历差别对象,依据差别的类型,依据对应对规定更新 VNode

React 的解决 render 的根本思维模式是每次一有变动就会去从新渲染整个利用。在 Virtual DOM 没有呈现之前,最简略的办法就是间接调用 innerHTML。Virtual DOM 厉害的中央并不是说它比间接操作 DOM 快,而是说不论数据怎么变,都会尽量以最小的代价去更新 DOM。React 将 render 函数返回的虚构 DOM 树与老的进行比拟,从而确定 DOM 要不要更新、怎么更新。当 DOM 树很大时,遍历两棵树进行各种比对还是相当耗性能的,特地是在顶层 setState 一个渺小的批改,默认会去遍历整棵树。只管 React 应用高度优化的 Diff 算法,然而这个过程依然会损耗性能.

React.Children.map 和 js 的 map 有什么区别?

JavaScript 中的 map 不会对为 null 或者 undefined 的数据进行解决,而 React.Children.map 中的 map 能够解决 React.Children 为 null 或者 undefined 的状况。

useEffect 和 useLayoutEffect 的区别

useEffect
基本上 90% 的状况下, 都应该用这个, 这个是在 render 完结后, 你的 callback 函数执行, 然而不会 block browser painting, 算是某种异步的形式吧, 然而 class 的 componentDidMount 和 componentDidUpdate 是同步的, 在 render 完结后就运行,useEffect 在大部分场景下都比 class 的形式性能更好.
useLayoutEffect
这个是用在解决 DOM 的时候, 当你的 useEffect 外面的操作须要解决 DOM, 并且会扭转页面的款式, 就须要用这个, 否则可能会呈现呈现闪屏问题, useLayoutEffect 外面的 callback 函数会在 DOM 更新实现后立刻执行, 然而会在浏览器进行任何绘制之前运行实现, 阻塞了浏览器的绘制.

React 中 constructor 和 getInitialState 的区别?

两者都是用来初始化 state 的。前者是 ES6 中的语法,后者是 ES5 中的语法,新版本的 React 中曾经废除了该办法。

getInitialState 是 ES5 中的办法,如果应用 createClass 办法创立一个 Component 组件,能够主动调用它的 getInitialState 办法来获取初始化的 State 对象,

var APP = React.creatClass ({getInitialState() {
    return { 
        userName: 'hi',
        userId: 0
     };
 }
})

React 在 ES6 的实现中去掉了 getInitialState 这个 hook 函数,规定 state 在 constructor 中实现,如下:

Class App extends React.Component{constructor(props){super(props);
      this.state={};}
  }
正文完
 0