React的首要思想是通过组件(Component)来开发应用,组件简单点讲就是指能够完成特定功能的独立,可复用的代码。
先来一个列子一个计时器的例子
点击按钮数字增加(当时学习深入浅出React和Redux 的一个小例子)
主要的信息通过注解写进代码里就不在陈述
首先在src下面创建一个ClickCounter.js的js文件
这里直接使用之前创建的那个react项目
首先贴出index.js代码
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
// 使用import导入了ClickCounter组件
import ClickCounter from './ClickCounter'
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(
//引入ClickCounter
<ClickCounter/>,
document.getElementById("root")
);
registerServiceWorker();
在这里ReactDom.render的第一个参数<ClickCounter/>就是一段jsx代码 ,React官网有对于JSX的介绍!ps:React判断一个元素是HTML元素还是React组件的原则就是看第一个字母是否是大写,如果我们在JSX中不用ClickCounter而是clickCounter,那就得不到我们想要的结果
然后在贴出ClickCounter.js的代码
//import React,{Component} from 'react';从react库中引入了React和Component
//Component作为所有的基类,提供了很多组件共有的工能
//引入React虽然我们没有使用,但是一定要导入这个React,
// 这是因为JSX最终会被编译成依赖于react的表达式
import React,{Component} from 'react';
//使用es6来创建一个叫ClickCounter的组件类,继承于Component
//ps: 在react出现支出使用的是React.createClass方式来创建组件类,
// 这种方式是一种过时的方法现在使用es6语法创建
class ClickCounter extends Component {
constructor(props) {
super(props);
this.onClickButton = this.onClickButton.bind(this);
this.state = {count:0};
}
onClickButton() {
this.setState({count: this.state.count + 1});
}
render() {
//定义样式
const counterStyle = {
margin:"20px",
color: 'green'
}
return (
//这个div引用我了counterStyle样式
<div style={counterStyle}>
<div>
Click Count:{this.state.count}
</div>
<button onClick={this.onClickButton}>点击我</button>
</div>
);
}
} export default ClickCounter;
1.props
接下来看第二个例子父组件与子组件相互传值
我们现在新增了两个js,ControlPanel.js和Counter.js两个js组件,其中CounterPanel是Counter的父组件
首先贴出ControlPanel的代码
import React, { Component } from 'react';
import Counter from './Counter.js';
const style = {
margin: '20px'
};
class ControlPanel extends Component {
render() {
console.log('enter ControlPanel render');
return (
<div style={style}>
{/*调用Counter不传参数*/}
<Counter caption="First"/>
{/*调用Counter组件传入initialValue = 10*/}
<Counter caption="Second" initialValue={10} />
{/*调用Counter组件传入initialValue = 20*/}
<Counter caption="Third" initialValue={20} />
<button onClick={ () => this.forceUpdate() }>
Click me to re-render!
</button>
</div>
);
}
}
export default ControlPanel;
在ControlPanel里面分别调用了三个counter组件,同时传入不同的参数值
import React,{Component} from 'react';
import PropTypes from 'prop-types';
const buttonStyle = {
margin: '10px'
};
class Counter extends Component{
constructor(props){
// 如果一个组件需要定义自己的构造函数,
// 一定要集合在狗仔函数的第一行通过super调用父类也就是React.component的构造函数,
// 如果在构造函数中没有调用superi(props),那么组件实例被构造之后,
// 类实例的成员函数就无法通过this.props访问到父组件传的值
//ps:给this.props赋值是React.Component构造函数的工作之一
//es6方法创建的React组件不能自动给我们绑定this到当前实例对象
super(props);
this.onClickIncrementButton = this.onClickIncrementButton.bind(this);
this.onClickDecrementButton = this.onClickDecrementButton.bind(this);
this.state = {
count:props.initialValue
}
}
// 实现按钮加一
onClickIncrementButton() {
this.setState({count: this.state.count + 1});
}
// 实现按钮减一
onClickDecrementButton() {
this.setState({count: this.state.count - 1});
}
render(){
const {caption} = this.props;
return(
<div>
<button style={buttonStyle} onClick={this.onClickIncrementButton}>+</button>
<button style={buttonStyle} onClick={this.onClickDecrementButton}>-</button>
<span>{caption} count:{this.state.count}</span>
</div>
)
}
}
// 在ES6方式定义的组件类中,可以通过增加类的propTypes书记邢来定义prop规格,
// 这不仅是一种声明,而且还是一种限制
Counter.propTypes = {
caption: PropTypes.string.isRequired,
initialValue: PropTypes.number
};
// 给变量附上默认值
Counter.defaultProps = {
initialValue: 0
};
export default Counter;
如果一个组件需要定义自己的构造函数, 一定要集合在狗仔函数的第一行通过super调用父类也就是React.component的构造函数,类实例的成员函数就无法通过this.props访问到父组件传的值ps:给this.props赋值是React.Component构造函数的工作之一s6方法创建的React组件不能自动给我们绑定this到当前实例对象
可以看到我们的项目
second我们传入参数10
thrid 我们传入参数20
propTypes
在ES6方式定义的组件类中,可以通过增加类的propTypes书记邢来定义prop规格, 这不仅是一种声明,而且还是一种限制
propTypes的使用:
1.先引入propTypes
import PropTypes from 'prop-types';
2.基础代码 ps:PropTypes.number你想要定义的格式,.isRequired是指是否必须穿这个参数
Counter.propTypes = {
caption: PropTypes.string.isRequired,
initValue: PropTypes.number
};
接下来我们修改ControlPanel的代码传入caption属性参数为int类型
{/*调用Counter组件传入initialValue = 20*/}
<Counter caption={20} initialValue={20} />
接下来我们可以在浏览器的Console窗口看到错误
接下来我们继续修改ControlPanel的代码删除caption属性
{/*调用Counter组件传入initialValue = 20*/}
<Counter initialValue={20} />
我们可以看到这里上面的爆出类型错误,下面则是没有传递该属性的错误
当然我们使用PropTypes是为了方便我们在开发过程中有一些不必要的错误,如果我们在生产环境就不需要这PropTypes了,使用PropTypes也会消耗一些cpu的资源
2.state
在React中驱动组件渲染过程除了prop,还有state,state代表组件的内部状态,由于React组件不能修改传入的prop,所以需要记录自身的数据变化,就要使用state ps:学过vue的话感觉state就像是vue里面data
下面是对state的初始化
this.state = {
count:props.initialValue
}
如果我们想修改组件的state就必须使用this.setState函数,而不能直接去修改this.stare
prop和state的对比
prop用于定义外部接口,state用于记录内部状态;
prop的赋值在外部世界使用组件时,state的赋值在组件内部;
·组件不应该改变prop的值,而state存在的目的就是让组件来改变的。
组件的state,就相当于组件的记忆,其存在意义就是被修改,每一次通过this.setState函数修改state就改变了组件的状态,然后通过渲染过程把这种变化体现出来。
3.子组件向父组件传值
首先贴出ControlPanel的代码
import React, { Component } from 'react';
import Counter from './Counter.js';
const style = {
margin: '20px'
};
class ControlPanel extends Component {
constructor(props) {
super(props);
this.onCounterUpdate = this.onCounterUpdate.bind(this);
this.initValues = [ 0, 10, 20];
const initSum = this.initValues.reduce((a, b) => a+b, 0);
this.state = {
sum: initSum
};
}
onCounterUpdate(newValue, previousValue) {
const valueChange = newValue - previousValue;
this.setState({ sum: this.state.sum + valueChange});
}
render() {
console.log('enter ControlPanel render');
return (
<div style={style}>
{/*调用Counter不传参数*/}
<Counter onUpdate={this.onCounterUpdate} caption="First"/>
{/*调用Counter组件传入initialValue = 10*/}
<Counter onUpdate={this.onCounterUpdate} caption="Second" initialValue={10} />
{/*调用Counter组件传入initialValue = 20*/}
<Counter onUpdate={this.onCounterUpdate} caption="threed" initialValue={20} />
<hr/>
<div>Total Count: {this.state.sum}</div>
{/*<button onClick={ () => this.forceUpdate() }>*/}
{/*Click me to re-render!*/}
{/*</button>*/}
</div>
);
}
}
export default ControlPanel;
主要新增了onCounterUpdate函数和和onUpdate的调用
接下来是counter的代码
import React,{Component} from 'react';
import PropTypes from 'prop-types';
const buttonStyle = {
margin: '10px'
};
class Counter extends Component{
constructor(props){
// 如果一个组件需要定义自己的构造函数,
// 一定要集合在狗仔函数的第一行通过super调用父类也就是React.component的构造函数,
// 如果在构造函数中没有调用superi(props),那么组件实例被构造之后,
// 类实例的成员函数就无法通过this.props访问到父组件传的值
//ps:给this.props赋值是React.Component构造函数的工作之一
//es6方法创建的React组件不能自动给我们绑定this到当前实例对象
super(props);
this.onClickIncrementButton = this.onClickIncrementButton.bind(this);
this.onClickDecrementButton = this.onClickDecrementButton.bind(this);
this.state = {
count:props.initialValue
}
}
// 实现按钮加一
onClickIncrementButton() {
this.setState({count: this.state.count + 1});
this.updateCount(this)
}
// 实现按钮减一
onClickDecrementButton() {
this.setState({count: this.state.count - 1});
this.updateCount(false)
}
updateCount(isIncrement){
const previousValue = this.state.count;
const newValue = isIncrement?previousValue+1:previousValue -1;
this.setState({count:newValue})
this.props.onUpdate(newValue,previousValue)
}
render(){
const {caption} = this.props;
return(
<div>
<button style={buttonStyle} onClick={this.onClickIncrementButton}>+</button>
<button style={buttonStyle} onClick={this.onClickDecrementButton}>-</button>
<span>{caption} count:{this.state.count}</span>
</div>
)
}
}
// 在ES6方式定义的组件类中,可以通过增加类的propTypes书记邢来定义prop规格,
// 这不仅是一种声明,而且还是一种限制
Counter.propTypes = {
caption: PropTypes.string.isRequired,
initialValue: PropTypes.number,
onUpdate: PropTypes.func
};
// 给变量附上默认值
Counter.defaultProps = {
initialValue: 0,
onUpdate:f => f
};
export default Counter;
主要提取了共同部分到updateCounth函数里, 然后通过调用this.props.onUpdate这个函数向父组件传递数据