参考 ant 组件库来学习。
    https://ant.design/components/icon-cn/
    https://www.iconfont.cn/collections/detail?spm=a313x.7781069.1998910419.d9df05512&cid=16957

    安装:
    npm install prop-types —save

    这是一个第三方库,用来检测Component props里属性的类型。
    例如,你可以这样去使用它:

    当你在Son组件里指定了属性的类型,比如optionalNumber必须为number类型,当从父组件传递的optionalNumber不是number的时候,就会报错,其实也就是给属性类型强类型了。

    1. Son.propTypes = {
    2. optionalArray: PropTypes.array,//检测数组类型
    3. optionalBool: PropTypes.bool,//检测布尔类型
    4. optionalFunc: PropTypes.func,//检测函数(Function类型)
    5. optionalNumber: PropTypes.number,//检测数字
    6. optionalObject: PropTypes.object,//检测对象
    7. optionalString: PropTypes.string,//检测字符串
    8. optionalSymbol: PropTypes.symbol,//ES6新增的symbol类型
    9. }

    Icon组件代码:

    static PropTypes用来规范组件,static defaultProps是设置组件属性的默认值。

    1. import React, { Component } from 'react';
    2. import '../../font/iconfont';
    3. import PropTypes from 'prop-types';
    4. class Icon extends Component {
    5. // 规范组件
    6. static PropTypes = {
    7. name: PropTypes.string
    8. }
    9. static defaultProps = {
    10. name: 'ABC'
    11. }
    12. render(){
    13. let {
    14. name,
    15. ...rest
    16. } = this.props;
    17. return (<div>
    18. <span {...rest} className={`icon iconfont icon-${name}`}></span>
    19. Icon
    20. </div>);
    21. }
    22. }
    23. export default Icon;

    APP组件中使用Icon:

    1. class App extends Component{
    2. render(){
    3. return (<div>
    4. <Icon onClick={() => alert('Hello')} style={{color: 'red'}} name={'Primary'}/>
    5. </div>);
    6. }
    7. }

    Button组件:
    书写样式:

    定义两个颜色变量。

    1. $primaryColor: blue;
    2. $warningColor: yellow;

    遵循BEM规范:—代表状态,__代表子元素。

    1. @import "../theme/index.scss";
    2. .react-ui__btn{
    3. &--primary{
    4. background: $primaryColor;
    5. color: #fff;
    6. background-color: #1890ff;
    7. border-color: #1890ff;
    8. text-shadow: 0 -1px 0 rgba(0,0,0,0.12);
    9. }
    10. &--warning{
    11. background: $warningColor;
    12. }
    13. }
    1. import Component from 'React';
    2. import PropTypes from 'prop-types';
    3. import Icon from '../Icon'
    4. class Button extends Component {
    5. static PropTypes = {
    6. icon: PropTypes.string,
    7. type: PropTypes.string
    8. }
    9. static defaultProps = {
    10. icon: 'submit',
    11. type: 'primary'
    12. }
    13. rednder(){
    14. const {
    15. icon,
    16. children,
    17. type,
    18. ...rest
    19. } = this.props;
    20. return (
    21. <button className={`react-ui__btn--${type}`}>
    22. <Icon name={icon}/>
    23. {children}
    24. </button>
    25. );
    26. }
    27. }

    受控组件和非受控组件:

    受控组件: react 接收控制权, 非react的手段不能对元素进行更改. 每一个操作都在react的监控内;必须有value和onChange。在React中,每当表单的状态发生变化时,都会被写入到组件的state中,这种组件在React被称为受控组件。受控组件中,组件渲染的状态与它的value或者checked相对应。React通过这种方式消除了组件的局部状态。React官方推荐使用受控组件。
    受控组件更新state流程:

    1. 1. 可以通过在初始state中设置表单的默认值。
    2. 2. 每当表单的值发生变化时,调用onChange事件处理器。
    3. 3. 事件处理器通过合成事件对象e拿到改变后的状态,并更新state
    4. 4. setState触发视图的重新渲染,完成表单组件值得更新。

    非受控组件: 元素的状态不受 react 控制;
    简单的说,如果一个表单组件没有value props(单选按钮和复选框对应的是checked props)就可以称为非受控组件。这样,我们可以使用defaultValuedefaultChecked来表示组件的默认状态。
    在React中,非受控组件是一种反模式,它的值不受组件自身的state或者props控制,通常需要为其添加ref prop来访问渲染后的底层DOM元素。
    在受控组件中,可以将用书输入的内容输出展示,而在受控组件中,如果不绑定onChange事件,我们在文本框中输入任何内容都不会展示。可以看到受控组件和非受控组件的最大区别就是,非受控组件状态并不会受应用状态的控制,应用中也多了局部组件状态,而受控组件的值来源于state。

    1. render(){
    2. return (<div>
    3. {this.state.name} - {this.state.age}
    4. <input value={this.state.name} />
    5. <input defaultValue={this.state.name} />
    6. </div>);
    7. }

    上面的例子中:
    如果受控组件(第一个input)没有onChange事件的话,用户在输入框里输入值,是输入不了的,显示不了;所以受控组件必须要有value和onchange属性。

    1. render(){
    2. return (<div>
    3. {this.state.name} - {this.state.age}
    4. <input value={this.state.name} onChange={(e) => this.setState({name: e.target.value})}/>
    5. <input defaultValue={this.state.name} />
    6. </div>);
    7. }

    上面的代码:受控组件(第一个input),当用户输入改变时,并不会引起第二个input输入框里值的变化,因为第二个input是非受控组件,它不受react state的控制,defaultValue给设置了默认值为state里的值,但当state值变化的时候,并不会引起输入框里值的变化。

    可以看到受控组件和非受控组件的最大区别就是,非受控组件状态并不会受应用状态的控制,应用中也多了局部组件状态,而受控组件的值来源于state。

    性能上的问题:**
    在受控组件中,每次表单的值发生变化都会调用一次onChange时间处理器,这会有一些性能消耗,任然不提倡在React中使用非受控组件。
    下面代码中,受控组件的变化,会触发组件重绘(会输出Will update);但是非受控组件输入值的改变并不会触发重绘,这导致了组件中出现了局部状态。

    1. class App extends Component{
    2. constructor(props){
    3. super(props);
    4. this.state = {
    5. name: 'David',
    6. age: 26
    7. };
    8. console.log(this.state);
    9. }
    10. value = 'hello';
    11. render(){
    12. return (<div>
    13. {this.state.name} - {this.state.age}
    14. <input value={this.state.name} onChange={(e) => this.setState({name: e.target.value})}/>
    15. <input defaultValue={this.value} onChange={(e) => {
    16. this.value = e.target.value;
    17. }}/>
    18. </div>);
    19. }
    20. componentWillUpdate(){
    21. console.log('will update!');
    22. }
    23. }

    Input组件示例代码:
    支持非受控组件,和受控组件。
    APP:

    1. import React, {Component} from 'react';
    2. import ReactDOM from 'react-dom';
    3. import * as serviceWorker from './serviceWorker';
    4. import Input from './ui/input/input.js';
    5. class App extends Component{
    6. constructor(props){
    7. super(props);
    8. this.state = {
    9. value: ''
    10. };
    11. }
    12. value = '1';
    13. render(){
    14. return (<div>
    15. <Input size='large'/>
    16. <Input size='middle'/>
    17. <Input size='small' rule={/^\d*$/} message={'Please input number!'} value={this.state.value} onChange={(e) => this.setState({value: e.target.value})}/>
    18. <Input size='small' defaultValue={this.value} onChange={ (e) => {
    19. this.value = e.target.value;
    20. }
    21. }/>
    22. </div>);
    23. }
    24. componentWillUpdate(){
    25. console.log('will update!');
    26. }
    27. }
    28. ReactDOM.render(<App />, document.getElementById('root'));
    29. // If you want your app to work offline and load faster, you can change
    30. // unregister() to register() below. Note this comes with some pitfalls.
    31. // Learn more about service workers: https://bit.ly/CRA-PWA
    32. serviceWorker.unregister();

    Input组件:

    1. import React, { Component } from 'react';
    2. import classNames from 'classnames';
    3. import propTypes from 'prop-types';
    4. import './input.scss';
    5. class Input extends Component {
    6. constructor(props){
    7. super(props);
    8. this.state = {
    9. focus: false,
    10. innerValue: ''
    11. };
    12. }
    13. static propTypes = {
    14. value: propTypes.string,
    15. onChange: propTypes.func,
    16. size: propTypes.string
    17. }
    18. static defaultProps = {
    19. size: 'middle',
    20. onChange: () => {}
    21. }
    22. get isControl(){
    23. return 'value' in this.props;
    24. }
    25. get value() {
    26. if (this.isControl){
    27. return this.props.value;
    28. }
    29. else {
    30. return this.state.innerValue;
    31. }
    32. }
    33. render(){
    34. const {
    35. focus
    36. } = this.state;
    37. const {
    38. children,
    39. size,
    40. onChange,
    41. rule = new RegExp(),
    42. message,
    43. ...rest
    44. } = this.props;
    45. let cls = classNames({
    46. 'react-ui__input': true,
    47. input: true,
    48. focus,
    49. [`size-${size}`]: true
    50. });
    51. return (
    52. <div>
    53. <div className={cls}>
    54. <input
    55. value={this.value}
    56. onFocus= {(e) => {
    57. this.setState({focus: true});
    58. }}
    59. onBlur= {(e) => {
    60. this.setState({focus: false});
    61. }}
    62. onChange={(e)=> {
    63. if(!this.isControl) {
    64. this.setState({
    65. innerValue: e.target.value
    66. });
    67. }
    68. this.props.onChange(e);
    69. }}
    70. />
    71. </div>
    72. <p>
    73. {!rule.test(this.value) && message}
    74. </p>
    75. </div>);
    76. }
    77. componentDidMount() {
    78. this.setState({
    79. innerValue: this.props.defaultValue
    80. });
    81. }
    82. }
    83. export default Input;

    Input 样式文件:

    1. @import '../theme/index.scss';
    2. .react-ui__input {
    3. border: 1px solid #d2d2d2;
    4. display: inline-block;
    5. input{
    6. border: none;
    7. outline: none;
    8. }
    9. &.focus {
    10. border-color: blue;
    11. }
    12. &.size-large input {
    13. font-size: 18px;
    14. line-height: 40px;
    15. }
    16. &.size-middle input{
    17. font-size: 14px;
    18. line-height: 30px;
    19. }
    20. &.size-small input {
    21. font-size: 12px;
    22. line-height: 20px;
    23. }
    24. &--primary{
    25. //background: $primaryColor;
    26. color: #fff;
    27. background-color: #1890ff;
    28. border-color: #1890ff;
    29. text-shadow: 0 -1px 0 rgba(0,0,0,0.12);
    30. }
    31. &--warning{
    32. background: $warningColor;
    33. }
    34. }

    Table组件:

    1. import React, {Component} from 'react';
    2. import ReactDOM from 'react-dom';
    3. import * as serviceWorker from './serviceWorker';
    4. import Table from './ui/table/table';
    5. const dataSource = [
    6. {
    7. name: 'David',
    8. age: 26,
    9. gender: 'Male'
    10. },
    11. {
    12. name: 'Lee',
    13. age: 20,
    14. gender: 'Male'
    15. }
    16. ];
    17. const columns = [
    18. {
    19. title: 'Name',
    20. dataIndex: 'name',
    21. key: 'name',
    22. render: (text, item, index)=>{
    23. return (<a href="#">{text}?{index}</a>);
    24. }
    25. },
    26. {
    27. title: 'Age',
    28. dataIndex: 'age',
    29. key: 'age'
    30. },
    31. {
    32. title: 'Gender',
    33. dataIndex: 'gender',
    34. key: 'gender'
    35. }
    36. ];
    37. class App extends Component{
    38. constructor(props){
    39. super(props);
    40. }
    41. render() {
    42. return (<div>
    43. <Table columns={columns} dataSource={dataSource}/>
    44. </div>);
    45. }
    46. componentWillUpdate(){
    47. console.log('will update!');
    48. }
    49. }
    50. ReactDOM.render(<App />, document.getElementById('root'));
    51. // If you want your app to work offline and load faster, you can change
    52. // unregister() to register() below. Note this comes with some pitfalls.
    53. // Learn more about service workers: https://bit.ly/CRA-PWA
    54. serviceWorker.unregister();
    1. import React, {Component} from 'react';
    2. import PropTypes from 'prop-types';
    3. class ColumnItem extends Component {
    4. render(){
    5. const {
    6. title
    7. } = this.props.item;
    8. return (<td>{title}</td>);
    9. }
    10. }
    11. class Columns extends Component {
    12. render(){
    13. const {
    14. columns
    15. } = this.props;
    16. return (<thead>
    17. <tr>
    18. {columns.map((item) => {
    19. return (<ColumnItem item={item} key={item.key}/>);
    20. })}
    21. </tr>
    22. </thead>);
    23. }
    24. }
    25. class DataSourceItem extends Component {
    26. render(){
    27. const {
    28. columns,
    29. dataItem,
    30. index
    31. } = this.props;
    32. const tds = columns.map((item) => <td> {item.render ? item.render(dataItem[item.dataIndex], dataItem, index) : dataItem[item.dataIndex]} </td>);
    33. return (<tr>
    34. {tds}
    35. </tr>);
    36. }
    37. }
    38. class DataSource extends Component {
    39. render(){
    40. const {
    41. dataSource,
    42. columns
    43. } = this.props;
    44. let trs = dataSource.map((item, index) => <DataSourceItem dataItem={item} index={index} columns={columns}/>);
    45. return (
    46. <tbody>
    47. {trs}
    48. </tbody>
    49. );
    50. }
    51. }
    52. class Table extends Component{
    53. static propTypes = {
    54. columns: PropTypes.array,
    55. dataSource: PropTypes.array
    56. }
    57. static defaultProps = {
    58. columns: [],
    59. dataSource: []
    60. }
    61. render(){
    62. const {
    63. columns,
    64. dataSource
    65. } = this.props;
    66. return (<div>
    67. <table border="1" cellPadding="0" cellSpacing="0">
    68. <Columns columns={columns}/>
    69. <DataSource dataSource={dataSource} columns={columns}/>
    70. </table>
    71. </div>)
    72. }
    73. }
    74. export default Table;