本文简单介绍了如何编写一个React 组件。为了快速演示,文章中出现的示例代码均用 react-create-app 官方推荐的脚手架快速搭建的项目中完成,react-create-app 传送门。
一、组件的介绍
Components let you split the UI into independent, reusable pieces, and think about each piece in isolation.
组件可以让你把UI切分成独立的、可复用的块去单独的考虑和开发
二、组件的写法
展示型组件(Pure Component 、函数组件)
展示型组件是用来展示样式的,他们不绑定任何东西且没有依赖性,通常被实现为无状态功能组件。
// BlogList.jsimport React from "react";
export const BlogList = bloglist => (
<ul>
{bloglist.map(({ body, author,id }) =>
<li key={id}>{body}-{author}</li>
)}
</ul>
)复制代码
使用class定义一个组件
import React from "react";
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}复制代码
三、组件的编写
Demo 1
以上面的展示型组件为例子,在用 react-create-app 生成的目录中创建components 文件夹用于存放我们的组件(react-create-app 的使用方法 传送们)
然后在我们的App.js 里面 引入 BlogList.js
最后在页面中查看
Demo 2
最近用了Angular Js 和 Vue, 在写表单的时候基于双向数据绑定,写起来是相当的happy,当然,React 的单向数据流也有着自己的优点。基于此我们可以对项目中常用的表单控件进行组件的封装,下面以checkbox
为列。
stap1:
首先我们在components
文件夹下面创建Checkbox
文件夹以及Checkbox.js
文件
stap2:
按照通常的写页面的方式编写Checkbox.js
文件,并在App.js 中引用
// Checkbox.js
import React,{Component} from 'react';
class CheckBox extends Component {
render() {
return (
<label><input type="checkbox" />点击我</label>
)
}
}export default CheckBox;
复制代码
// App.js
import CheckBox from './compoents/CheckTest/CheckTest'
....
return (
<div className="App">
.....
<div>
<CheckBox></CheckBox>
</div>
</div>
);复制代码
这样我们的Checkbox 组件就被加载到了页面,但是label 被我们写死了‘点击我’,怎么实现动态替换呢?
// Checkbox.js
<span>
{this.props.children !== undefined ? this.props.children : null}
</span>
复制代码
// App.js
render() {
return (
<div className="App">
...
<div>
<CheckBox>新的点击我</CheckBox>
</div>
</div>
);
}
复制代码
现在查看,页面中的点击我
就替换成了新的点击我
了,到目前为止,实现了一个无状态的Checkbox,但是在开发中,我们要获取到checkbox 的选中状态,如果有多个checkbox ,我们还要得到选中的是哪几个checkbox。
stap3:
// Checkbox.js
import React from "react"import "./CheckBox.less"
class CheckBox extends React.Component {
constructor(props) {
super(props)
this.checkCheckBox = this.checkCheckBox.bind(this);
this.state = {
value: props.value || ''
}
}
checkCheckBox() {
const onChange = this.props.onChange;
const value = this.props.value;
if (onChange) {
onChange(value);
}
}
render() {
let {value} = this.state;
return (
<label>
<input value={value} type="checkbox" onClick={this.checkCheckBox}/>
{this.props.children !== undefined ? this.props.children : null}
</label>
)
}}export default CheckBox;复制代码
上述代码,我们的Checkbox接收父组件传下来的value 和 onChange 方法,在点击的时候,把组件的value 值传上去,然后在父组件中获取
class App extends Component {
onChange=(value)=>{
console.log('当前点击',value)
}
render() {
return (
<div className="App">
...
<div>
<CheckBox value='checkbox1' onChange={this.onChange}>checkbox1</CheckBox>
<CheckBox value='checkbox2' onChange={this.onChange}>checkbox2</CheckBox>
</div>
</div>
);
}}复制代码
这样在每次点击的时候可以在控制台看到
stap4:
现在有个新的需求,在checkbox 加载的时候有的checkbox 是默认选中的,怎么样在父组件控制选中状态呢。
class CheckBox extends React.Component {
constructor(props) {
super(props)
this.checkCheckBox = this.checkCheckBox.bind(this);
this.state = {
is_checked: props.checked || false,
value: props.value || ''
}
}
render() {
let {is_checked,value} = this.state;
return (
<label>
<input value={value} type="checkbox" onClick={this.checkCheckBox}
checked={is_checked}/>
{this.props.children !== undefined ? this.props.children : null}
</label>
)
}}复制代码
这样,我们的Checkbox 组件就是受控的了,也就是说它的选中状态是与父组件传下来的checked 属性相关连的,现在在次点击按钮,发现无论怎么点也选不上了。
stap5:
我们的Checkbox 组件一般都不会单独的加载,尤其是在选择项很多的业务场景中,checkbox 组件都是分组出现的,我们希望在点击的时候能在父组件拿到选中的Checkbox 的数组,并且改变checkbox 的选中状态。
首先我们再在components 文件夹下面创建一个CheckboxGroup文件夹,同样在下面新建CheckboxGroup.js
。首先贴出来代码,然后再来分析
import React from "react"
import "./CheckboxGroup.less"
import PropTypes from 'prop-types';
class CheckboxGroup extends React.Component {
constructor(props) {
super(props)
this.state = {
value: props.value || props.defaultValue || [],
};
this.toggleOption = this.toggleOption.bind(this);
}
componentWillReceiveProps(nextProps) {
if ('value' in nextProps) {
this.setState({
value: nextProps.value || [],
});
}
}
getChildContext() {
return {
checkboxGroup: {
toggleOption: this.toggleOption,
value: this.state.value,
},
};
}
toggleOption(option) {
const optionIndex = this.state.value.indexOf(option.value);
const value = [...this.state.value];
if (optionIndex === -1) {
value.push(option.value);
} else {
value.splice(optionIndex, 1);
}
if (!('value' in this.props)) {
this.setState({value});
}
const onChange = this.props.onChange;
if (onChange) {
onChange(value);
}
}
render() {
const {children, className} = this.props
return (
<div className={className}>
{children}
</div>
)
}}
CheckboxGroup.childContextTypes = { checkboxGroup: PropTypes.any,};export default CheckboxGroup复制代码
代码分析:从render
开始看
render() {
const {children, className} = this.props
return (
<div className={className}>
{children}
</div>
)
}复制代码
这段代码很熟悉,和上面的Checkbox
组件一样,会加载组件包裹的元素,也就是说,CheckboxGroup
组件包裹了Checkbox
,那是怎样接管Checkbox
的状态的呢?
getChildContext() {
return {
checkboxGroup: {
toggleOption: this.toggleOption,
value: this.state.value,
},
};
}复制代码
getChildContext
可以传递给自组件属性,需要主意的是,getChildContext
指定的传递给子组件的属性需要先通过 childContextTypes
来指定,不然会产生错误。
CheckboxGroup.childContextTypes = {
checkboxGroup: PropTypes.any,
};复制代码
然后他会getChildContent中返回子组件需要的属性,同时在componentWillReceiveProps 生命周期中监听props 的改变从而更新CheckBox 的 选中状态。
同时,CheckBox.js 里面也要做相应的修改
import React from "react"
import "./CheckBox.less"
import PropTypes from 'prop-types';
import CheckboxGroup from '../CheckboxGroup/CheckboxGroup.js'
class CheckBox extends React.Component {
constructor(props) {
super(props)
this.checkCheckBox = this.checkCheckBox.bind(this);
this.state = {
is_checked: props.checked || false,
value: props.value || ''
}
}
checkCheckBox() {
const {checkboxGroup} = this.context;
if (checkboxGroup) {
checkboxGroup.toggleOption({label: this.props.children, value: this.props.value})
} else {
const onChange = this.props.onChange;
const value = this.props.value;
if (onChange) {
onChange(value);
}
}
}
render() {
let {is_checked, value} = this.state;
const {checkboxGroup} = this.context;
if (checkboxGroup) {
is_checked = checkboxGroup.value.indexOf(this.props.value) !== -1;
}
return (
<label>
<input value={value} type="checkbox" checked={is_checked}
onClick={this.checkCheckBox}/>
{this.props.children !== undefined ? this.props.children : null}
</label>
)
}}
CheckBox.Group = CheckboxGroup;
CheckBox.contextTypes = {
checkboxGroup: PropTypes.any,
}export default CheckBox;
复制代码
这样,在点击的时候会先判断 this.context 中是否有 checkboxGroup属性,如果有,就调用checkboxGroup属性 中的toggleOption方法,并把当前的value 传上去,由 CheckboxGroup组件去控制,同时在渲染的时候也会判断,从而决定选中状态是由谁来决定。然后去App.js 中引用。
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import CheckBox from './compoents/CheckBox/CheckBox'
const CheckboxGroup = CheckBox.Group;
class App extends Component {
state={
checkList:[]
}
selectCheckBtn=(values)=>{
console.log(values)
this.setState({
checkList:values
})
}
render() {
const {checkList} = this.state
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
<CheckboxGroup value={checkList} onChange={this.selectCheckBtn}>
<CheckBox value={'1'}>按钮1</CheckBox>
<CheckBox value={'2'}>按钮2</CheckBox>
<CheckBox value={'3'}>按钮3</CheckBox>
<CheckBox value={'4'}>按钮4</CheckBox>
</CheckboxGroup>
</div>
);
}}
export default App;
复制代码
最后,打开控制台,点击按钮。
这里,我们便完成了一个简单的checkBox 组件。
总结
写一个组件很容易,但是写好一个组件就不是那么容易的事了,React 也有一些现在比较成熟的UI组件库,比如蚂蚁金服的Antd Design,可以打开看看里面的源码。学习下他们的设计思想。