60.如果在构造函数中使用 setState()
会发生什么?
当你使用 setState()
时,除了设置状态对象之外,React 还会重新渲染组件及其所有的子组件。你会得到这样的错误:Can only update a mounted or mounting component.。因此我们需要在构造函数中使用 this.state
初始化状态。
61.索引作为键的影响是什么?
Keys 应该是稳定的,可预测的和唯一的,这样 React 就能够跟踪元素。
在下面的代码片段中,每个元素的键将基于列表项的顺序,而不是绑定到即将展示的数据上。这将限制 React 能够实现的优化。
{todos.map((todo, index) =>
<Todo
{...todo}
key={index}
/>
)}
假设 todo.id
对此列表是唯一且稳定的,如果将此数据作为唯一键,那么 React 将能够对元素进行重新排序,而无需重新创建它们。
{todos.map((todo) =>
<Todo {...todo}
key={todo.id} />
)}
62.如果在初始状态中使用 props 属性会发生什么?
如果在不刷新组件的情况下更改组件上的属性,则不会显示新的属性值,因为构造函数函数永远不会更新组件的当前状态。只有在首次创建组件时才会用 props 属性初始化状态。
以下组件将不显示更新的输入值:
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
records: [],
inputValue: this.props.inputValue
};
}
render() {
return <div>{this.state.inputValue}</div>
}
}
在 render 方法使用使用 props 将会显示更新的值:
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
record: []
}
}
render() {
return <div>{this.props.inputValue}</div>
}
}
63.为什么在 DOM 元素上展开 props 需要小心?
当我们展开属性时,我们会遇到添加未知 HTML 属性的风险,这是一种不好的做法。相反,我们可以使用属性解构和...rest
运算符,因此它只添加所需的 props 属性。例如,
const ComponentA = () =>
<ComponentB isDisplay={true} className={'componentStyle'} />
const ComponentB = ({ isDisplay, ...domProps }) =>
<div {...domProps}>{'ComponentB'}</div>
64.如何 memoize(记忆)组件?
有可用于函数组件的 memoize 库。例如 moize
库可以将组件存储在另一个组件中。(类似Vue的keep-alive组件)
import moize from 'moize'
import Component from './components/Component' // this module exports a non-memoized component
const MemoizedFoo = moize.react(Component)
const Consumer = () => {
<div>
{'I will memoize the following entry:'}
<MemoizedFoo/>
</div>
}
65.什么是 CRA 及其好处?
create-react-app
CLI 工具允许你无需配置步骤,快速创建和运行 React 应用。
让我们使用 CRA 来创建 Todo 应用:
# Installation
$ npm install -g create-react-app
# Create new project
$ create-react-app todo-app
$ cd todo-app
# Build, test and run
$ npm run build
$ npm run test
$ npm start
它包含了构建 React 应用程序所需的一切:
- React, JSX, ES6, 和 Flow 语法支持。
- ES6 之外的语言附加功能,比如对象扩展运算符。
- Autoprefixed CSS,因此你不在需要 -webkit- 或其他前缀。
- 一个快速的交互式单元测试运行程序,内置了对覆盖率报告的支持。
- 一个实时开发服务器,用于警告常见错误。
- 一个构建脚本,用于打包用于生产中包含 hashes 和 sourcemaps 的 JS、CSS 和 Images 文件。
66.在 mounting 阶段生命周期方法的执行顺序是什么?
在创建组件的实例并将其插入到 DOM 中时,将按以下顺序调用生命周期方法。
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()
67.在 React v16 中,哪些生命周期方法将被弃用?
以下生命周期方法将成为不安全的编码实践,并且在异步渲染方面会更有问题。
componentWillMount()
componentWillReceiveProps()
componentWillUpdate()
从 React v16.3 开始,这些方法使用 UNSAFE_
前缀作为别名,未加前缀的版本将在 React v17 中被移除。
68.生命周期方法 getDerivedStateFromProps()
的目的是什么?
新的静态 getDerivedStateFromProps()
生命周期方法在实例化组件之后以及重新渲染组件之前调用。它可以返回一个对象用于更新状态,或者返回 null
指示新的属性不需要任何状态更新。
class MyComponent extends React.Component {
static getDerivedStateFromProps(props, state) {
// ...
}
}
此生命周期方法与 componentDidUpdate()
一起涵盖了 componentWillReceiveProps()
的所有用例。
69.生命周期方法 getSnapshotBeforeUpdate()
的目的是什么?
新的 getSnapshotBeforeUpdate()
生命周期方法在 DOM 更新之前被调用。此方法的返回值将作为第三个参数传递给componentDidUpdate()
。
class MyComponent extends React.Component {
getSnapshotBeforeUpdate(prevProps, prevState) {
// ...
}
}
此生命周期方法与 componentDidUpdate()
一起涵盖了 componentWillUpdate()
的所有用例。
70.推荐的组件命名方法是什么?
建议通过引用命名组件,而不是使用 displayName
。
使用 displayName
命名组件:
export default React.createClass({
displayName: 'TodoApp',
// ...
})
推荐的方式:
export default class TodoApp extends React.Component {
// ...
}
71.在组件类中方法的推荐顺序是什么?
- 组件创建阶段:
static 开头的 只会执行一次
constructor 构造器 只会执行一次
getDerivedStateFromProps: 当子组件接收到新的props会执行,作用:将传递的props映射到state里面 会执行多次
render 构建虚拟dom,但是此时虚拟dom还没有渲染到页面 会执行多次
componentDidMount:组建的虚拟dom已经挂载到页面 只会执行一次
- 组件运行阶段:根据 props 属性 或 state 状态的改变,有选择性的执行0到多次
getDerivedStateFromProps:组件将要接收到新的props属性
shouldComponentUpdate:组件是否需要被更新,返回值是true或者false。此时可以获取最新的props和state数据
render: 重新更新渲染组件的虚拟dom
getSnapshotBeforeUpdate:在最近一次渲染提交到 DOM 节点之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置),返回值将作为参数传递给componentDidUpdate
componentDidUpdate: 组件完成了更新,此时页面已经是最新的了
- 组件销毁阶段:只执行一次
componentWillUnmount: 组件将要被销毁,此时组件还可以被使用
72.什么是 switching 组件?
switching 组件是渲染多个组件之一的组件。我们需要使用对象将 prop 映射到组件中。
例如,以下的 switching 组件将基于 page
属性显示不同的页面:
import HomePage from './HomePage'
import AboutPage from './AboutPage'
import ServicesPage from './ServicesPage'
import ContactPage from './ContactPage'
const PAGES = {
home: HomePage,
about: AboutPage,
services: ServicesPage,
contact: ContactPage
}
const Page = (props) => {
const Handler = PAGES[props.page] || ContactPage
return <Handler {...props} />
}
// The keys of the PAGES object can be used in the prop types to catch dev-time errors.
Page.propTypes = {
page: PropTypes.oneOf(Object.keys(PAGES)).isRequired
}
73.为什么我们需要将函数传递给 setState() 方法?
这背后的原因是 setState()
可能是一个异步操作。出于性能原因,React 会对状态更改进行批处理,因此在调用 setState()
方法之后,状态可能不会立即更改。这意味着当你调用 setState()
方法时,你不应该依赖当前状态,因为你不能确定当前状态应该是什么。这个问题的解决方案是将一个函数传递给 setState()
,该函数会以上一个状态作为参数。通过这样做,你可以避免由于 setState()
的异步性质而导致用户在访问时获取旧状态值的问题。
假设初始计数值为零。在连续三次增加操作之后,该值将只增加一个。
// assuming this.state.count === 0
this.setState({ count: this.state.count + 1 })
this.setState({ count: this.state.count + 1 })
this.setState({ count: this.state.count + 1 })
// this.state.count === 1, not 3
如果将函数传递给 setState()
,则 count 将正确递增。
this.setState((prevState, props) => ({
count: prevState.count + props.increment
}))
// this.state.count === 3 as expected
74.你认为状态更新(state)是如何合并的?
当你在组件中调用 setState() 方法时,React 会将提供的对象合并到当前状态。例如,让我们以一个使用帖子和评论详细信息的作为状态变量的 Facebook 用户为例:
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
现在,你可以独立调用 setState() 方法,单独更新状态变量:
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
如上面的代码段所示,this.setState({comments})
只会更新 comments 变量,而不会修改或替换 posts 变量。
75.在 React 中什么是严格模式?
React.StrictMode
是一个有用的组件,用于突出显示应用程序中的潜在问题。就像 <Fragment>
,<StrictMode>
一样,它们不会渲染任何额外的 DOM 元素。它为其后代激活额外的检查和警告。这些检查仅适用于开发模式。
import React from 'react'
function ExampleApplication() {
return (
<div>
<Header />
<React.StrictMode>
<div>
<ComponentOne />
<ComponentTwo />
</div>
</React.StrictMode>
<Footer />
</div>
)
}
在上面的示例中,strict mode 检查仅应用于 <ComponentOne>
和 <ComponentTwo>
组件。
76.React 中支持哪些指针事件?
Pointer Events 提供了处理所有输入事件的统一方法。在过去,我们有一个鼠标和相应的事件监听器来处理它们,但现在我们有许多与鼠标无关的设备,比如带触摸屏的手机或笔。我们需要记住,这些事件只能在支持 Pointer Events 规范的浏览器中工作。
目前以下事件类型在 React DOM 中是可用的:
onPointerDown
onPointerMove
onPointerUp
onPointerCancel
onGotPointerCapture
onLostPointerCaptur
onPointerEnter
onPointerLeave
onPointerOver
onPointerOut
77.在 React v16 中是否支持自定义 DOM 属性?
是的,在过去 React 会忽略未知的 DOM 属性。如果你编写的 JSX 属性 React 无法识别,那么 React 将跳过它。例如:
<div mycustomattribute={'something'} />
在 React 15 中将在 DOM 中渲染一个空的 div:
<div />
在 React 16 中,任何未知的属性都将会在 DOM 显示:
<div mycustomattribute='something' />
这对于应用特定于浏览器的非标准属性,尝试新的 DOM APIs 与集成第三方库来说非常有用。
78.constructor 和 getInitialState 有什么区别?
当使用 ES6 类时,你应该在构造函数中初始化状态,而当你使用 React.createClass()
时,就需要使用 getInitialState()
方法。
使用 ES6 类:
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = { /* initial state */ }
}
}
使用 React.createClass()
:
const MyComponent = React.createClass({
getInitialState() {
return { /* initial state */ }
}
})
注意: 在 React v16 中 React.createClass()
已被弃用和删除,请改用普通的 JavaScript 类。
79.是否可以在不调用 setState 方法的情况下,强制组件重新渲染?
默认情况下,当组件的状态或属性改变时,组件将重新渲染。如果你的 render()
方法依赖于其他数据,你可以通过调用 forceUpdate()
来告诉 React,当前组件需要重新渲染。
component.forceUpdate(callback)
建议避免使用 forceUpdate()
,并且只在 render()
方法中读取 this.props
和 this.state
。
80.在 JSX 中如何进行循环?
你只需使用带有 ES6 箭头函数语法的 Array.prototype.map
即可。例如,items
对象数组将会被映射成一个组件数组:
<tbody>
{items.map(item => <SomeComponent key={item.id} name={item.name} />)}
</tbody>
你不能使用 for
循环进行迭代:
<tbody>
for (let i = 0; i < items.length; i++) {
<SomeComponent key={items[i].id} name={items[i].name} />
}
</tbody>
这是因为 JSX 不能在JSX表达式中使用语句。