单一职责原则在react组件设计中依然管用,尤其是维护一个大型的页面时。但也不是事无巨细都需要拆分。根据所谓"高内聚低耦合"的思想,逻辑紧密的组件是不适合拆分的。
react的组件基础,应当时时复习。必要时自己写一写。
在react中组件有很多种方法,es5下createClass在React16以后的版本全部废弃。
而上文计数器的实例,就是一种标准的创建,通常包含交互与状态。此外还有两种创建的思路:
通常用于创建无状态的组件。它接受props作为参数,和标准创建相比,没有继承React.Component。
function Hello({name}){
return <>{hello,${name}}</>
}
Hello.PropTypes={
name:React.propTypes.string
}
Hello.defaultPropTypes={
name:'world'
}
在维护一个拥有众多子组件的页面时,也许有些组件跟你当前的功能八杆子打不着。但最后的结果也参与了渲染。react15.3之后就出现了PrueComponent的概念。
PureComponent改变了生命周期方法shouldComponentUpdate
,并且它会自动检查组件是否需要重新渲染。这时,只有PureComponent检测到state
或者props
发生变化时,PureComponent才会调用render
方法,因此,你不用手动写额外的检查,就可以在很多组件中改变state
。
如果让你手写一个PureComponnet:
// 只做浅拷贝比较
const shallowCompares=(a,b)=>{
if(a==b){return true;}
if(Object.keys(a).length!==Object.keys(b).length){
return false;
}
return Object.keys(a).every(k=>a[k]==b[k]);
}
class Pure extends Component{
shouldComponentUpdate(nextProps,nextState){
const {props,state}=this;
return !shallowCompares(props,nextProps) && !shallowCompares(state,nextState)
}
}
现在稍微改写下上节的计数器:
import React, { Component , PureComponent} from 'react';
class Show extends PureComponent {
render() {
console.log('渲染了')
return (
<div>
1
</div>
)
}
}
export default class ClickCounter extends Component {
constructor(props){
super(props);
this.state={
count:0
}
}
handleClick=()=>{
this.setState((preState)=>{
return {
counter:preState.count++
}
})
}
render() {
return (
<div>
<Show/>
<button onClick={this.handleClick}>点我</button>
</div>
)
}
}
这个案例中,作为一个纯组件,Show无论怎么点击都不会触发渲染。
在React中,prop(property的简写)是从外部传递给组件的数据,一个React组件通过定义自己能够接受的prop就定义了自己的对外公共接口。每个React组件都是独立存在的模块,组件之外的一切都是外部世界,外部世界就是通过prop来和组件对话的。
既然prop是组件的对外接口,那么就应该有某种方式让组件声明自己的接口规范。简单说,一个组件应该可以规范以下这些方面:
React通过propTypes来支持这些功能。在ES6方法定义的组件类中,可以通过增加类的propTypes属性来定义prop规格,这不只是声明,而且是一种限制,在运行时和静态代码检查时,都可以根据propTypes判断外部世界是否正确地使用了组件的属性,是否必须等。
Hello.PropTypes={
name:React.propTypes.string.isRequired;
}
Hello.defaultPropTypes={
name:'world'
}
当然这只是开发辅助功能,生产环境应该考虑去掉。
"不要直接修改组件的state",是react的常识。
阅读以下代码
import React, { Component } from 'react'
import PropTypes from 'prop-types';
const styles = {
counter: {
width: 200,
display: 'flex'
},
showbox: {
width: 80,
textAlign: 'center'
}
}
class ClickCounter extends Component {
constructor(props) {
super(props);
this.state = {
count: props.initValue
}
}
handleIncrease = () => {
this.setState((preState) => {
return {
counter: preState.count++
}
})
}
handleDecrease = () => {
this.setState((preState) => {
return {
counter: preState.count--
}
})
}
render() {
return (
<div style={styles.counter}>
<button onClick={this.handleDecrease}>-</button>
<div style={styles.showbox}>{this.state.count}</div>
<button onClick={this.handleIncrease}>+</button>
</div>
)
}
}
ClickCounter.propTypes = {
initValue: PropTypes.number.isRequired
}
ClickCounter.defaultProps = {
initValue: 0
}
export default ClickCounter
此时界面是:
如果我们把新增的逻辑改为:
handleIncrease = () => {
this.state.count++;
// this.setState((preState) => {
// return {
// counter: preState.count++
// }
// })
}
点击3次+号你会发现增加怎样都不成功。点击减法时,state一下变成了2。
直接修改this.state的值,虽然事实上改变了组件的内部状态,但只是野蛮地修改了state,却没有驱动组件进行重新渲染,既然组件没有重新渲染,当然不会反应this.state值的变化;而this.setState()函数所做的事情,首先是改变this.state的值,然后驱动组件经历更新过程,这样才有机会让this.state里新的值出现在界面上。
•prop用于定义外部接口,state用于记录内部状态;•prop的赋值在外部世界使用组件时,state的赋值在组件内部;•组件不应该改变prop的值,而state存在的目的就是让组件来改变的。