“差劲的程序员操心代码,优秀的程序员操心数据结构和它们之间的关系 。 ”
一一Linus Torvalds, Linux 创始人
首先附上package.json的依赖包,版本问题导致包导入问题,需要自行调整
{
"name": "read-react-redux",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-scripts": "1.1.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"devDependencies": {
"react-router": "3.0.5"
}
}
本文是笔者阅读深入浅出React+Redux过程中,纪录整理自己知识点文章。(PS:一本书差不多重点在于20%,其他80%是作者为了出版凑得字数。)
毫无疑问,如何组织数据是程序的最重要问题。
React 组件的数据分为两种, prop 和 state ,无论 prop 或者 state 的改变,都可能引发组件的重新渲染,那么,设计一个组件的时候,什么时候选择用 prop 什么时候选择state 呢?
其实原则很简单, prop 是组件的对外接口, state 是组件的内部状态,对外用prop ,内部用 state。
prop ( property 的简写)是从外部传递给组件的数据, 一个 React 组件通过定义自己能够接受的 prop 就定义了自己的对外公共接口 。
(1) 给 prop 赋值
<SampleButton
id="sample" borderWidth={2} onClick={onButtonClick}
style={{color: "red"}}
/>
上面例子中,创建了名为 SampleButton 的组件实例,使用了名字分别为 id 、 borderWidth 、 onClick 和 style 的 prop ,看起来, React 组件的 prop 很像是 HTML 元素的属性,
不过, HTML 组件属性的值都是字符串类型,即使是内嵌 JavaScript ,也依然是字符串形式表示代码 。
React 组件的 prop 所能支持的类型则丰富得多,除了字符串,可以是任何一种 JavaScript 语言支持的数据类型
。
当 prop 的类型不是字符串类型时,在 JSX 中必须用花括号{}把 prop 值包住,所以 style 的值有两层花括号,外层花括号代表是 JSX 的语法
,内层的花括号代表这是一个对象常量
。
重点PS
因为prop可以是函数,内部组件可以通过它反馈数据给外部组件。
(2)读取 prop 值
ControlPanel.jsx
import React, { Component } from 'react';
import Counter from './Counter';
class ControlPanel extends Component {
render() {
return (
<div>
"First" initValue={0}/>
"Second" initValue={10}/>
"Third" initValue={20}/>
div>
)
}
}
export default ControlPanel;
(2)然后增加内部子组件Counter.jsx
(2.1)构造调用props
import React, { Component } from 'react';
/*** 暂时去掉stateless组件写法
const Counter= ({ caption, initValue= 0 })=> {
return (
{caption}, {initValue}
);
};
export default Counter;*/
export default class Counter extends Component {
constructor(props){
super(props);
}
}
上面代码在构造函数的第一行通过 super(props)
调用父类也就是 React.Component 的构造函数。如果在构造函数中没有调用 super(props),那么组件实例被构造之后,类实例的所有成员函数就无法通过 this.props 访问到父组件传递过来的 props 值。
(2.2)绑定方法this
constructor(props){
super(props);
+ this.clickIncrementHandler= this.clickIncrementHandler.bind(this);
+ this.clickDecrementHandler= this.clickDecrementHandler.bind(this);
}
+ clickIncrementHandler(){}
+ clickDecrementHandler(){}
上面代码。ES6方法创造的 React 组件类并不自动给我们绑定 this 到当前实例对象。这是区别createClass(ps:这是过时方法)
自动绑定的。
当然,如果你不想用bind的方式绑定。也提供一种高逼格
的写法(箭头函数)
constructor(props){
super(props);
}
clickIncrementHandler=() =>{}
clickDecrementHandler=() =>{}
(3)propTypes 检查
propTypes声明组件的接口规范,简单理解
这个组件支持哪些 prop
每个 prop 应该是什么样的格式
// 这是原来v15版本,PropType的导入方式和约束方式
import React, { Component, PropTypes } from 'react';
Counter.PropTypes= {
caption: PropTypes.string.isRequired,
initValue: PropTypes.number.isRequired,
}
// v16的版本移除后变为
import PropTypes from 'prop-types';
Counter.propTypes= { // 注意大小写
caption: PropTypes.string.isRequired,
initValue: PropTypes.number.isRequired,
}
上面代码。caption 带上了 isRequried ,这表示使用 Counter组件必须要指定 caption ;
但是。没有 propTypes 定义,组件依然能够正常工作,而且,即使在上面 propTypes 检查出错的情况下,组件依旧能工作 。 也就是说 propTypes 检查只是一个辅助开发的功能,并不会改变组件的行为 。
所以定义类的 propTypes 属性,无疑是要占用一些代码空间,而且 propTypes 检查也是要消耗 CPU 计算资源的 。这在开发环境可以。但是产品上线环境就是不理想。
所以可以通过npm 安装babel-react-optimize 在发布环境使用。 。
state 代表组件的内部状态 。 由于 React组件不能修改传入的 prop ,所以需要记录自身数据变化,就要使用 state 。
(1)初始化 state
this.state= {
count: props.initValue || 0,
}
上面代码,我们在构造函数中初始化count的值。但是构造函数中放入判断逻辑,本身就不是一件太美观的事。这里你可以使用React 的 defaultProps 功能
+ Counter.defaultProps = {
+ initValue: 0
+ };
这样,即使 Counter 的使用者没有指定 initValue ,在组件中就会收到一个默认的属
性值 0。(stateless可以使用对象的解构)
(2)读取和更新 state
clickIncrementHandler=() =>{
+ this.setState({count : this.state.count + 1}, ()=>{
+ console.log('finish');
+ });
};
通过setState修改state的值。第二个参数是成功回调(试图渲染成功回调)
PS:直接修改 this.state 的值,虽然事实上改变了组件的内部状态,但只是野蛮地修改了
state ,却没有驱动组件进行重新渲染
总结一下 prop 和 state 的区别:
1) prop 用于定义外部接口, state 用于记录内部状态;
2) prop 的赋值在外部世界使用组件时, state 的赋值在组件内部;
3) 组件不应该改变 prop 的值,而 state 存在的目的就是让组件来改变的 。