写点什么

react 常见面试题

  • 2022 年 8 月 02 日
  • 本文字数:7795 字

    阅读完需:约 26 分钟

组件之间传值

  • 父组件给子组件传值

  • 在父组件中用标签属性的=形式传值

  • 在子组件中使用 props 来获取值

  • 子组件给父组件传值

  • 在组件中传递一个函数

  • 在子组件中用 props 来获取传递的函数,然后执行该函数

  • 在执行函数的时候把需要传递的值当成函数的实参进行传递

  • 兄弟组件之间传值

  • 利用父组件

  • 先把数据通过 【子组件】===》【父组件】

  • 然后在数据通过 【父组件】===〉【子组件】

  • 消息订阅

  • 使用 PubSubJs 插件

为何 React 事件要自己绑定 this

在 React 源码中,当具体到某一事件处理函数将要调用时,将调用 invokeGuardedCallback 方法。


function invokeGuardedCallback(name, func, a) {  try {    func(a);  } catch (x) {    if (caughtError === null) {      caughtError = x;    }  }}
复制代码


事件处理函数是直接调用的,并没有指定调用的组件,所以不进行手动绑定的情况下直接获取到的 this 是不准确的,所以我们需要手动将当前组件绑定到 this 上

使用箭头函数(arrow functions)的优点是什么

  • 作用域安全:在箭头函数之前,每一个新创建的函数都有定义自身的 this 值(在构造函数中是新对象;在严格模式下,函数调用中的 this 是未定义的;如果函数被称为“对象方法”,则为基础对象等),但箭头函数不会,它会使用封闭执行上下文的 this 值。

  • 简单:箭头函数易于阅读和书写

  • 清晰:当一切都是一个箭头函数,任何常规函数都可以立即用于定义作用域。开发者总是可以查找 next-higher 函数语句,以查看 this 的值

解释 React 中 render() 的目的。

每个 React 组件强制要求必须有一个 render()。它返回一个 React 元素,是原生 DOM 组件的表示。如果需要渲染多个 HTML 元素,则必须将它们组合在一个封闭标记内,例如 <form><group><div> 等。此函数必须保持纯净,即必须每次调用时都返回相同的结果。

指出(组件)生命周期方法的不同

  • componentWillMount -- 多用于根组件中的应用程序配置

  • componentDidMount -- 在这可以完成所有没有 DOM 就不能做的所有配置,并开始获取所有你需要的数据;如果需要设置事件监听,也可以在这完成

  • componentWillReceiveProps -- 这个周期函数作用于特定的 prop 改变导致的 state 转换

  • shouldComponentUpdate -- 如果你担心组件过度渲染,shouldComponentUpdate 是一个改善性能的地方,因为如果组件接收了新的 prop, 它可以阻止(组件)重新渲染。shouldComponentUpdate 应该返回一个布尔值来决定组件是否要重新渲染

  • componentWillUpdate -- 很少使用。它可以用于代替组件的 componentWillReceivePropsshouldComponentUpdate(但不能访问之前的 props)

  • componentDidUpdate -- 常用于更新 DOM,响应 prop 或 state 的改变

  • componentWillUnmount -- 在这你可以取消网络请求,或者移除所有与组件相关的事件监听器

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={};    }  }复制代码
复制代码

对 React Hook 的理解,它的实现原理是什么

React-Hooks 是 React 团队在 React 组件开发实践中,逐渐认知到的一个改进点,这背后其实涉及对类组件函数组件两种组件形式的思考和侧重。


(1)类组件: 所谓类组件,就是基于 ES6 Class 这种写法,通过继承 React.Component 得来的 React 组件。以下是一个类组件:


class DemoClass extends React.Component {  state = {    text: ""  };  componentDidMount() {    //...  }  changeText = (newText) => {    this.setState({      text: newText    });  };
render() { return ( <div className="demoClass"> <p>{this.state.text}</p> <button onClick={this.changeText}>修改</button> </div> ); }}
复制代码
复制代码


可以看出,React 类组件内部预置了相当多的“现成的东西”等着我们去调度/定制,state 和生命周期就是这些“现成东西”中的典型。要想得到这些东西,难度也不大,只需要继承一个 React.Component 即可。


当然,这也是类组件的一个不便,它太繁杂了,对于解决许多问题来说,编写一个类组件实在是一个过于复杂的姿势。复杂的姿势必然带来高昂的理解成本,这也是我们所不想看到的。除此之外,由于开发者编写的逻辑在封装后是和组件粘在一起的,这就使得类组件内部的逻辑难以实现拆分和复用。


(2)函数组件:函数组件就是以函数的形态存在的 React 组件。早期并没有 React-Hooks,函数组件内部无法定义和维护 state,因此它还有一个别名叫“无状态组件”。以下是一个函数组件:


function DemoFunction(props) {  const { text } = props  return (    <div className="demoFunction">      <p>{`函数组件接收的内容:[${text}]`}</p>    </div>  );}复制代码
复制代码


相比于类组件,函数组件肉眼可见的特质自然包括轻量、灵活、易于组织和维护、较低的学习成本等。


通过对比,从形态上可以对两种组件做区分,它们之间的区别如下:


  • 类组件需要继承 class,函数组件不需要;

  • 类组件可以访问生命周期方法,函数组件不能;

  • 类组件中可以获取到实例化后的 this,并基于这个 this 做各种各样的事情,而函数组件不可以;

  • 类组件中可以定义并维护 state(状态),而函数组件不可以;


除此之外,还有一些其他的不同。通过上面的区别,我们不能说谁好谁坏,它们各有自己的优势。在 React-Hooks 出现之前,类组件的能力边界明显强于函数组件。


实际上,类组件和函数组件之间,是面向对象和函数式编程这两套不同的设计思想之间的差异。而函数组件更加契合 React 框架的设计理念: React 组件本身的定位就是函数,一个输入数据、输出 UI 的函数。作为开发者,我们编写的是声明式的代码,而 React 框架的主要工作,就是及时地把声明式的代码转换为命令式的 DOM 操作,把数据层面的描述映射到用户可见的 UI 变化中去。这就意味着从原则上来讲,React 的数据应该总是紧紧地和渲染绑定在一起的,而类组件做不到这一点。函数组件就真正地将数据和渲染绑定到了一起。函数组件是一个更加匹配其设计理念、也更有利于逻辑拆分与重用的组件表达形式。


为了能让开发者更好的的去编写函数式组件。于是,React-Hooks 便应运而生。


React-Hooks 是一套能够使函数组件更强大、更灵活的“钩子”。


函数组件比起类组件少了很多东西,比如生命周期、对 state 的管理等。这就给函数组件的使用带来了非常多的局限性,导致我们并不能使用函数这种形式,写出一个真正的全功能的组件。而 React-Hooks 的出现,就是为了帮助函数组件补齐这些(相对于类组件来说)缺失的能力。


如果说函数组件是一台轻巧的快艇,那么 React-Hooks 就是一个内容丰富的零部件箱。“重装战舰”所预置的那些设备,这个箱子里基本全都有,同时它还不强制你全都要,而是允许你自由地选择和使用你需要的那些能力,然后将这些能力以 Hook(钩子)的形式“钩”进你的组件里,从而定制出一个最适合你的“专属战舰”。

React 的严格模式如何使用,有什么用处?

StrictMode 是一个用来突出显示应用程序中潜在问题的工具。与 Fragment 一样,StrictMode 不会渲染任何可见的 UI。它为其后代元素触发额外的检查和警告。可以为应用程序的任何部分启用严格模式。例如:


import React from 'react';function ExampleApplication() {  return (    <div>      <Header />      <React.StrictMode>                <div>          <ComponentOne />          <ComponentTwo />        </div>      </React.StrictMode>            <Footer />    </div>  );}
复制代码
复制代码


在上述的示例中,不会对 HeaderFooter 组件运行严格模式检查。但是,ComponentOneComponentTwo 以及它们的所有后代元素都将进行检查。


StrictMode 目前有助于:


  • 识别不安全的生命周期

  • 关于使用过时字符串 ref API 的警告

  • 关于使用废弃的 findDOMNode 方法的警告

  • 检测意外的副作用

  • 检测过时的 context API

为什么要使用 React. Children. map( props. children,( )=>)而不是 props. children. map ( ( ) => )?

因为不能保证 props. children 将是一个数组。以下面的代码为例。


<Parent>    <h1>有课前端网</h1></Parent>
复制代码


在父组件内部,如果尝试使用 props.children. map 映射子对象,则会抛出错误,因为 props. children 是一个对象,而不是一个数组。如果有多个子元素, React 会使 props.children 成为一个数组,如下所示。


<Parent>  <h1>有课前端网</h1>  <h2>前端技术学习平台</h2></Parent>;//不建议使用如下方式,在这个案例中会抛出错误。
class Parent extends Component { render() { return <div> {this.props.children.map((obj) => obj)}</div>; }}
复制代码


建议使用如下方式,避免在上一个案例中抛出错误。


class Parent extends Component {  render() {    return <div> {React.Children.map(this.props.children, (obj) => obj)}</div>;  }}
复制代码

hooks 和 class 比较的优势?

一、更容易复用代码


二、清爽的代码风格+代码量更少


缺点


状态不同步


不好用的 useEffect,

类组件和函数组件有何不同?

解答


在 React 16.8 版本(引入钩子)之前,使用基于类的组件来创建需要维护内部状态或利用生命周期方法的组件(即componentDidMountshouldComponentUpdate)。基于类的组件是 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 题)。


进一步阅读


什么是 React Context?

Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性。

react 实现一个全局的 dialog

import React, { Component } from 'react';import { is, fromJS } from 'immutable';import ReactDOM from 'react-dom';import ReactCSSTransitionGroup from 'react-addons-css-transition-group';import './dialog.css';let defaultState = {  alertStatus:false,  alertTip:"提示",  closeDialog:function(){},  childs:''}class Dialog extends Component{  state = {    ...defaultState  };  // css动画组件设置为目标组件  FirstChild = props => {    const childrenArray = React.Children.toArray(props.children);    return childrenArray[0] || null;  }  //打开弹窗  open =(options)=>{    options = options || {};    options.alertStatus = true;    var props = options.props || {};    var childs = this.renderChildren(props,options.childrens) || '';    console.log(childs);    this.setState({      ...defaultState,      ...options,      childs    })  }  //关闭弹窗  close(){    this.state.closeDialog();    this.setState({      ...defaultState    })  }  renderChildren(props,childrens) {    //遍历所有子组件    var childs = [];    childrens = childrens || [];    var ps = {        ...props,  //给子组件绑定props        _close:this.close  //给子组件也绑定一个关闭弹窗的事件           };    childrens.forEach((currentItem,index) => {        childs.push(React.createElement(            currentItem,            {                ...ps,                key:index            }        ));    })    return childs;  }  shouldComponentUpdate(nextProps, nextState){    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))  }     render(){    return (      <ReactCSSTransitionGroup        component={this.FirstChild}        transitionName='hide'        transitionEnterTimeout={300}        transitionLeaveTimeout={300}>        <div className="dialog-con" style={this.state.alertStatus? {display:'block'}:{display:'none'}}>            {this.state.childs}        </div>      </ReactCSSTransitionGroup>    );  }}let div = document.createElement('div');let props = {   };document.body.appendChild(div);let Box = ReactD复制代码
复制代码


子类:


//子类jsximport React, { Component } from 'react';class Child extends Component {    constructor(props){        super(props);        this.state = {date: new Date()};  }  showValue=()=>{    this.props.showValue && this.props.showValue()  }  render() {    return (      <div className="Child">        <div className="content">           Child           <button onClick={this.showValue}>调用父的方法</button>        </div>      </div>    );  }}export default Child;复制代码
复制代码


css:


.dialog-con{    position: fixed;    top: 0;    left: 0;    width: 100%;    height: 100%;    background: rgba(0, 0, 0, 0.3);}复制代码
复制代码

state 和 props 区别是啥?

  • state 是组件自己管理数据,控制自己的状态,可变;

  • props 是外部传入的数据参数,不可变;

  • 没有 state 的叫做无状态组件,有 state 的叫做有状态组件;

  • 多用 props,少用 state,也就是多写无状态组件。

在构造函数调用 super 并将 props 作为参数传入的作用

在调用 super() 方法之前,子类构造函数无法使用 this 引用,ES6 子类也是如此。


将 props 参数传递给 super() 调用的主要原因是在子构造函数中能够通过 this.props 来获取传入的 props


传递了 props


class MyComponent extends React.Component {  constructor(props) {    super(props);    console.log(this.props); // { name: 'sudheer',age: 30 }  }}
复制代码


没传递 props


class MyComponent extends React.Component {  constructor(props) {    super();    console.log(this.props); // undefined    // 但是 Props 参数仍然可用    console.log(props); // Prints { name: 'sudheer',age: 30 }  }  render() {    // 构造函数外部不受影响    console.log(this.props); // { name: 'sudheer',age: 30 }  }}
复制代码

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} />  }}
复制代码

高阶组件的应用场景

权限控制


利用高阶组件的 条件渲染 特性可以对页面进行权限控制,权限控制一般分为两个维度:页面级别页面元素级别


// HOC.js    function withAdminAuth(WrappedComponent) {        return class extends React.Component {            state = {                isAdmin: false,            }            async 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... } componentWillMount() { // fetching data } render() { // render page with data } } export default withAdminAuth(PageA);
复制代码


可能你已经发现了,高阶组件其实就是装饰器模式在 React 中的实现:通过给函数传入一个组件(函数或类)后在函数内部对该组件(函数或类)进行功能的增强(不修改传入参数的前提下),最后返回这个组件(函数或类),即允许向一个现有的组件添加新的功能,同时又不去修改该组件,属于 包装模式(Wrapper Pattern) 的一种。


什么是装饰者模式:在不改变对象自身的前提下在程序运行期间动态的给对象添加一些额外的属性或行为


可以提高代码的复用性和灵活性


再对高阶组件进行一个小小的总结:


  • 高阶组件 不是组件 一个把某个组件转换成另一个组件的 函数

  • 高阶组件的主要作用是 代码复用

  • 高阶组件是 装饰器模式在 React 中的实现


封装组件的原则


封装原则


1、单一原则:负责单一的页面渲染


2、多重职责:负责多重职责,获取数据,复用逻辑,页面渲染等


3、明确接受参数:必选,非必选,参数尽量设置以_开头,避免变量重复


4、可扩展:需求变动能够及时调整,不影响之前代码


5、代码逻辑清晰


6、封装的组件必须具有高性能,低耦合的特性


7、组件具有单一职责:封装业务组件或者基础组件,如果不能给这个组件起一个有意义的名字,证明这个组件承担的职责可能不够单一,需要继续抽组件,直到它可以是一个独立的组件即可

什么是控制组件?

在 HTML 中,表单元素如 <input><textarea><select>通常维护自己的状态,并根据用户输入进行更新。当用户提交表单时,来自上述元素的值将随表单一起发送。而 React 的工作方式则不同。包含表单的组件将跟踪其状态中的输入值,并在每次回调函数(例如onChange)触发时重新渲染组件,因为状态被更新。以这种方式由 React 控制其值的输入表单元素称为受控组件

redux 是如何更新值得

用户发起操作之后,dispatch 发送 action ,根据 type,触发对于的 reducer,reducer 就是一个纯函数,接收旧的 state 和 action,返回新的 state。通过 subscribe(listener)监听器,派发更新。

用户头像

还未添加个人签名 2022.07.31 加入

还未添加个人简介

评论

发布
暂无评论
react常见面试题_React_helloworld1024fd_InfoQ写作社区