前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >react面试题

react面试题

作者头像
用户7162790
发布2022-03-23 15:14:24
6940
发布2022-03-23 15:14:24
举报
文章被收录于专栏:森屿暖树
  • 父组件可以向子组件传递props,props中带有初始化子组件的数据,还有回调函数
  • 子组件的state发生变化时,在子组件的事件处理函数中,手动触发父函数传递进来的回调函数,同时时将子组件的数据传递回去
代码语言:javascript
复制
// 父组件
class Team extends PureComponent {
  constructor() {
    super()
    this.state = {
      name: 'oyo'
    }
  }
  render() {
    return (
      <div>Team onwer is:
        <People
          name={this.state.name}
          onClick={name => {
            this.setState({ name })
          }}
        >
        </People>
      </div>
    );
  }
};
// 子组件
class People extends PureComponent {
  render() {
    return (
      <span
        onClick={() => {() => this.props.onClick('hello')}
      >
        {this.props.name}
      </span>
    )
  }
});

调用setState之后发生了什么? 在代码中调用setState函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个UI界面。在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。 扩展1: setState的第一个参数除了对象,还能传什么? ---函数,参数为当前state

代码语言:javascript
复制
setState(state => {
  return {
    num: state.num + 1
  }
})

扩展2: setState的第二个参数有什么用?---回调

代码语言:javascript
复制
this.setState(
  { username: 'tylermcginnis33' },
  () => console.log('setState has finished and the component has re-rendered.')
)

类似于vue中的this.$nextTick(),该函数会在setState函数调用完成并且组件重渲染后被调用

扩展3: setState的时候如果两次state值没有发生变化,一定不会造成调用render吗? ---会

代码语言:javascript
复制
import React from 'react'
class Test extends React.Component{
  constructor(props) {
    super(props);
    this.state = {
      Number:1//设state中Number值为1
    }
  }
  //这里调用了setState但是并没有改变setState中的值
  handleClick = () => {
     const preNumber = this.state.Number
     this.setState({
        Number:this.state.Number
     })
  }
  render(){
    //当render函数被调用时,打印当前的Number
    console.log(this.state.Number)
    return(
             {this.state.Number}
           )
  }
}
export default Test

Refs 是 React 提供给我们的安全访问 DOM 元素或者某个组件实例的句柄。我们可以为元素添加ref属性然后在回调函数中接受该元素在 DOM 树中的句柄. 我们可以使用ref的句柄来调用自组件的方法,甚至去setState, 但并不推荐这么去操作

扩展: 能否说一下有在工作中具体使用的案例

代码语言:javascript
复制
class CustomForm extends Component {
  handleSubmit = () => {
    console.log("Input Value: ", this.input.value)
  }
  render () {
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          type='text'
          ref={(input) => this.input = input} />
        <button type='submit'>Submit</button>
      </form>
    )
  }
}

扩展2: 使用ref去获取input值(利用DOM存放表单数据)后操作的我们称为非受控组件Uncontrolled Component,那么其与受控组件Controlled Component,直接使用state去操作input值有什么区别?

代码语言:javascript
复制
class ControlledForm extends Component {
  state = {
    username: ''
  }
  updateUsername = (e) => {
    this.setState({
      username: e.target.value,
    })
  }
  handleSubmit = () => {}
  render () {
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          type='text'
          value={this.state.username}
          onChange={this.updateUsername} />
        <button type='submit'>Submit</button>
      </form>
    )
  }
}

实际开发中我们并不提倡使用非受控组件,因为实际情况下我们需要更多的考虑表单验证、选择性的开启或者关闭按钮点击、强制输入格式等功能支持,而此时我们将数据托管到 React 中有助于我们更好地以声明式的方式完成这些功能。引入 React 或者其他 MVVM 框架最初的原因就是为了将我们从繁重的直接操作 DOM 中解放出来。

扩展三: ref的三种使用方式

  • 字符串 ref="myRef" (不推荐, react会在下个版本移除)
  • 回调 ref={ref => this.myRef = ref}
  • React.createRef
代码语言:javascript
复制
class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef()
  }
  componentDidMount() {
    console.log(this.myRef.current)
  }
  render() {
    return <div ref={this.myRef} />
  }
}

扩展四: 有没有理解过React.forwardRef?

React V16.3中,react推出forwardRef

再类似HOC高阶组件中, 我们如果需要把ref绑定至真正需要调用的子组件而不是HOC组件上时, 我们需要借助React.forwardRef去传递ref的引用

代码语言:javascript
复制
function HOC(Wrapper) {
  class AddModal extends Component {
    constructor(props) {
      super(props)
      this.state = {
        name: '',
      }
    }
    render() {
      return <Wrapper {...this.props} ref={this.props.forwardedRef} data={this.state} />
    }
  }
  return React.forwardRef((props, ref) => <AddModal {...props} forwardedRef={ref} />)
}
export default HOC

Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。 提升优化react中的diff算法,避免不必要的节点摧毁与重建

代码语言:javascript
复制
render () {
  return (
    <ul>
      {this.state.todoItems.map(({task, uid}) => {
        return <li key={uid}>{task}</li>
      })}
    </ul>
  )
}

在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系,因此我们绝不可忽视转换函数中 Key 的重要性。

扩展一: 如果不加key值或者key值相同的情况可能会造成什么问题?

  • react会进行报错提示
  • 性能下降
  • key值相同的情况有可能会造成数据更新时,数据的错乱

如下为react在源码中对key的比较,如果不同则会直接更新

代码语言:javascript
复制
// 用来判定两个element需不需要更新
// 这里的key是我们createElement的时候可以选择性的传入的。
// 用来标识这个element,当发现key不同时,我们就可以直接重新渲染,不需要去更新了。
var _shouldUpdateReactComponent = function (prevElement, nextElement) {
  if (prevElement != null && nextElement != null) {
    var prevType = typeof prevElement;
    var nextType = typeof nextElement;
    if (prevType === 'string' || prevType === 'number') {
      return nextType === 'string' || nextType === 'number';
    } else {
      return
        nextType === 'object' &&
        prevElement.type === nextElement.type &&
        prevElement.key === nextElement.key;
    }
  }
  return false;
}

扩展二: 循环的时候利用index去作为key值好不好?

key做为DOM节点标识,如果是前后两次arr分别为[1,2,3,4]和[5,6,7,8]和前后两次arr分别为[1,2,3,4]和[4,3,2,1]的情况,很明显前者可以认为是DOM改变了,后者可以认为是DOM节点的位移操作,那么对于第一种情况来说index作为key和没有key值无区别,但是第二种情况用index作为key值效果没有比用数据本身作为key值好,这里大家可以按照以上方式打印去看一下.所以结论是如果你的数据能确保唯一性,就用数据本身作为key值吧

jquery中的事件会被绑定在原生节点本身,而在react中会被统一绑定到document去代理

扩展: 知道react中事件大致的注册以及触发的原理吗

注册时react会首先判断该组件上props是否是event事件,若是则绑定到document上,回调函数是dispatchEvent,将绑定了事件的react组件实例的rooNodeId(虚拟dom的唯一标识)取出来,作为key值,对应的回调函数作为value值存为一个对象

触发时事件冒泡传递到document的时候,会触发dispatchEvent的执行,根据目标实例的递归向上寻找目标实例的父元素和祖先元素,存到数组path中,然后遍历path,获取rooNodeId作为key值将之前存事件的对象value值全部取出,最后挨个执行回调

代码语言:javascript
复制
//对于新的属性,需要写到dom节点上
for (propKey in nextProps) {
  //对于事件监听的属性我们需要特殊处理
  if (/^on[A-Za-z]/.test(propKey)) {
    var eventType = propKey.replace('on', '');
    //以前如果已经有,说明有了监听,需要先去掉
    lastProps[propKey] &&
    $(document).undelegate(
      '[data-reactid="' + this._rootNodeID + '"]',
      eventType,
      lastProps[propKey]);
    //针对当前的节点添加事件代理,以_rootNodeID为命名空间
    $(document).delegate(
      '[data-reactid="' + this._rootNodeID + '"]',
      eventType + '.' + this._rootNodeID,
      nextProps[propKey]);
    continue;
  }
 
  if (propKey == 'children') continue;
 
  //添加新的属性,或者是更新老的同名属性
  $('[data-reactid="' + this._rootNodeID + '"]').prop(propKey, nextProps[propKey])
}

用作上下文的的数据储存, react中数据以props传输,但在组件嵌套过深的情况下, pros需要逐层传递,相当的麻烦.

在新版本的react中, 使用React.createContext进行创建context对象.其会返回Provider(提供数据的父组件)以及Consumer(消费数据的子组件)两个对象进行使用,react-redux目前就是以这样的方式进行数据传递

代码语言:javascript
复制
// 创建context对象
import React from 'react'
import ReactDOM from 'react-dom'
const MyContext = React.createContext({
  name: '凤嘉',
})
// 父组件使用Provider
class App extends React.Component {
  render() {
    return (
      <MyContext.Provider store={{ name: '凤嘉' }}>
        <Header />
      </MyContext.Provider>
    )
  }
}
 
// 其中任意子组件消费store
class Title extends React.Component {
  render() {
    return (
      <MyContext.Consumer>
        {store => (
          <h1>
            {store.children}
          </h1>
        )}
      </MyContext.Consumer>
    )
  }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档