初识React你应该知道的一些点|博客系列
# 初识React你应该知道的一些点
# 组件
组件允许你将UI拆分为独立可复用的代码片段,每个片段进行独立构思。
组件,从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素。
-
函数组件
function Welcome (props) { return <h2>Hello, {props.name}</h2>; }@前端进阶之旅: 代码已经复制到剪贴板 -
class组件
class Welcome extends React.Component { render () { return <h2>Hello, {this.props.name}</h2>; } }@前端进阶之旅: 代码已经复制到剪贴板
以上两个组件是等效的。
注意: 组件名称必须以大写字母开头。
React 会将以小写字母开头的组件视为原生 DOM 标签。例如,代表 HTML 的 div 标签,而 则代表一个组件,并且需在作用域内使用 Welcome。
# State
正确的使用setState():
-
不要直接修改
State:// 错误 this.state.msg = 'LinDaiDai' // 正确 this.setState({ msg: 'LinDaiDai' })@前端进阶之旅: 代码已经复制到剪贴板 -
State的更新可能是异步的,如果更新的state中有依赖到state或者props则可以用函数:// 错误 this.setState({ counter: this.state.counter + this.props.increment }); // 正确 this.setState((state, props) => ({ counter: state.counter + props.increment })); // 使用普通函数也可以 this.setState(function(state, props) { return { counter: state.counter + props.increment }; });@前端进阶之旅: 代码已经复制到剪贴板 -
State的更新可能会被合并当你调用
setState()的时候,React 会把你提供的对象合并到当前的 state。例如,你的 state 包含几个独立的变量:
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})完整保留了this.state.posts, 但是完全替换了this.state.comments。https://react.docschina.org/docs/state-and-lifecycle.html#state-updates-are-merged
# 事件
解决事件中this绑定的问题,如果不想用bind显式绑定的话,可以使用下面两种方式:
-
class fields语法:class LoggingButton extends React.Component { // 此语法确保 `handleClick` 内的 `this` 已被绑定。 // 注意: 这是 *实验性* 语法。 handleClick = () => { console.log('this is:', this); } render() { return ( <button onClick={this.handleClick}> Click me </button> ); } }@前端进阶之旅: 代码已经复制到剪贴板 -
在回调中使用箭头函数
class LoggingButton extends React.Component { handleClick() { console.log('this is:', this); } render() { // 此语法确保 `handleClick` 内的 `this` 已被绑定。 return ( <button onClick={() => this.handleClick()}> Click me </button> ); } }@前端进阶之旅: 代码已经复制到剪贴板此语法问题在于每次渲染
LoggingButton时都会创建不同的回调函数。在大多数情况下,这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染。我们通常建议在构造器中绑定或使用 class fields 语法来避免这类性能问题。
# 组合VS继承
React中并没有类似于Vue中的slot,不过可以靠prop来实现:
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
);
}
function App() {
return (
<SplitPane
left={
<Contacts />
}
right={
<Chat />
} />
);
}
# 语法需要注意的点
# 1. 数字0依旧会被渲染
false, null, undefined是合法的子元素,但它们并不会渲染,因此可以作为判断JSX表达式渲染结果:
<div>
{showHeader && <Header />}
<Content />
</div>
但是数字0,仍然会被 React 渲染。例如,以下代码并不会像你预期那样工作,因为当 props.messages 是空数组时,0 仍然会被渲染:
<div>
{props.messages.length &&
<MessageList messages={props.messages} />
}
</div>
解决方式:确保 && 之前的表达式总是布尔值:
<div>
{props.messages.length > 0 &&
<MessageList messages={props.messages} />
}
</div>
# 2. 元素和组件
元素:React 元素是构成 React 应用的基础砖块。元素描述了你在屏幕上想看到的内容。React 元素是不可变对象。例如:
const element = <h1>Hello, world</h1>;
组件:React 组件是可复用的小的代码片段,它们返回要在页面中渲染的 React 元素。例如:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
# 如何阻止组件不更新
以下生命周期默认是返回true,只要将其改为false即不更新。
shouldComponentUpdate(nextProps, nextState) {
return false;
}
# 其它API
# forceUpdate()强制组件重新渲染
默认情况下,当组件的 state 或 props 发生变化时,组件将重新渲染。如果 render() 方法依赖于其他数据,则可以调用 forceUpdate() 强制让组件重新渲染。
调用 forceUpdate() 将致使组件调用 render() 方法,此操作会跳过该组件的 shouldComponentUpdate()。
但其子组件会触发正常的生命周期方法,包括 shouldComponentUpdate() 方法。如果标记发生变化,React 仍将只更新 DOM。
注意⚠️:
通常你应该避免使用 forceUpdate(),尽量在 render() 中使用 this.props 和 this.state。
# Class属性
# defaultProps
defaultProps 可以为 Class 组件添加默认 props。这一般用于 props 未赋值,但又不能为 null 的情况:
class CustomButton extends React.Component {
// ...
}
CustomButton.defaultProps = {
color: 'blue'
};
上述CustomButton被调用时如果没有提供props.color,则默认为blue。
此时如果传递了null的话,则就会是null:
render() {
return <CustomButton color={null} /> ; // props.color 将保持是 null
}
# displayName
displayName 字符串多用于调试消息。通常,你不需要设置它,因为它可以根据函数组件或 class 组件的名称推断出来。
用处:
调试时需要显示不同的名称或创建高阶组件。displayName可以方便我们在使用Chrome浏览器插件的情况下通过审查元素看到具体是哪个组件。
例如你要审查的这个元素是一个HOC产物,如下一个简单的案例🌰:
LoginHOC.tsx:
import React, { Component } from 'react';
export default function LoginHoc (WrappedComponent) {
class InnerComponent extends Component {
render () {
return (
<WrappedComponent {...this.props}></WrappedComponent>
)
}
}
return InnerComponent;
}
Blog.tsx:
import React, { Component } from 'react';
import LoginHOC from './LoginHOC';
class Blog extends Component {
render () {
return (
<div className='blog'>
<button>我是按钮</button>
</div>
)
}
}
export default LoginHOC(Blog);
在App.js中引用:
