| 创建组件
| render
| props、propType、state、setState()、replaceState()
| 获取DOM this.refs.xxx 或 ReactDOM.findDOMNode(this.refs.xxx)
| React.Children 获取父组件传的DOM
| mixin

React

React包括几个概念: 1、组件       2、JSX     3、虚拟DOM     4、单向数据绑定        5、受控组件

1、核心是封装组件,只关注UI,状态变更重新渲染整个组件

2、JSX: HTML代码可以直接嵌到JS代码中,这就是React提出的叫JSX的语法,原来前端以表现和逻辑层分离为主,但HTML是组件的一部分所以不能分割,JSX 将动态值放到 { ... }

3、单向数据流: 当数据更新会渲染整个app

    React的渲染方式: 用户输入 ~> 从API获取数据 ~> 将数据传给顶层组件 ~> React将每个组件渲染出来, 不会象MVC一样的双向数据绑定、和数据模型的脏值检测、不会有确切的DOM操作

4、虚拟DOM树: React重建一个DOM树,找到与上一个版本的DOM的差异,计算出新的DOM更新操作,从操作队列中指执行DOM更新操作

    http://www.alloyteam.com/2015/04/%E5%89%8D%E6%B2%BF%E6%8A%80%E6%9C%AF%E8%A7%A3%E5%AF%86-virtualdom/      // 前沿技术解密——VirtualDOM

5、IE浏览器要在8.0版本以上

rrc 创建带redux组件
rcc 创建react组件
rca 创建action
reducer 创建reduer

react引用方式

// Component作为所有组件的基类,提供很多组件共有的功能
import React, { Component } from 'react';
import { reactDom } from 'react-dom';

// 定义组件
class SimpleComponent extends Component {
    render(){
        return <div> React,我们来了... </div>;
    }
}
export default SimpleComponent;

// 组件渲染
reactDom.render(<HelloMessage />, document.getElementById('app'));

生命周期

组件的生命周期三个过程

    装载过程(Mount) - 把组件第一次在DOM树中渲染的过程

    更新过程(Update) - 当组件被重新渲染的过程

    卸载过程(Unmount) - 组件从DOM中删除的过程


一、装载过程

    1、constructor - ES6每个类的构造函数,创建一个组件就会先调用对应的构造函数

    2、getInitialState() - 初始化this.state,(ES6不起作用)

    3、getDefaultProps - 设置默认的props (ES6不起作用)

    4、componentWillMount() - 组件挂载前(调用render方法之前触发, 没有任何渲染,就算调用了this.setState()也不会引发绘制)

    5、render() - 创建虚拟dom,进行diff算法,更新dom树,如果不需要渲染可以将render函数返回一个null或false

    6、componentDidMount() - 组件渲染后已经装载到DOM树中,这时候可以refs操作获取或操作DOM节点


二、更新过程(当props或state被修改时就会引发组件的更新过程)

    1、componentWillReceiveProps(nextProps) - 当父组件改变了传递给子组件props的值,这时候子组件此方法会被调用

    2、shouldComponentUpdate(nextProps, nextState) - react性能环节,父组件传给子组件的props值改变,子组件中的shouldComponentUpdata()方法会先调用,前后两个props相同返回false阻止更新,不需要在创建新的dom树在进行diff算法,默认为true

        shouldComponentUpdate(nextProps, nextState){
            return nextProps.name === nextState.name        // 如果返回false就算父组件传递的props值改变,也不会执行render
        }

    3、componentWillUpdate() - 与装载过程相同 

    4、render() - 与装载过程相同

    5、componentDidUpdate() - 与装载过程相同


三、卸载组件

    1、componentWillUnmount() - 卸载前调用

JSX语法

一、注释

    {/* 这里用于单多行注释 */}


二、根元素只能有一个

    class ComponentDemo extends Component {
        render(){
            // 以下写法直接报错
            return (
                <div>
                    hello
                </div>
            );
        }
    }


三、属性名不能和 js 关键字冲突

    1、class => className

    2、read => readOnly

    3、for => htmlFrom 不能直接使用for

        <div htmlFor='male'>Male</div>


四、JSX spread

    { ...spreadObj } 可以直接将spreadObj的对象传递过去

    import SpreadDemo from './components/spread';
    const spreadObj = {
        name: 'siguang',
        carType: '卡宴'
    }
    render(<SpreadDemo {...spreadObj} />, document.querySelector('#app'));


五、渲染HTML

    const elements = '<p>aaa</p><p>bbb</p><p>ccc</p>';
    <p dangerouslySetInnerHTML={{ __html: elements }}></p>

http://lib.csdn.net/article/react/22655

class和style

一、class

    方法1:
        import './admin.css'
        render() {
            return (
                <div className="container">            // 注意这里的值为字符串而不是{}
                    <div className="sild">xxxx</div>
                </div>
            );
        }

    方法2:
        import styles from './admin.css'
        render() {
            return (
                <div>
                    <div className={style.sild}>xxxx</div>
                </div>
            );
        }


二、style 行内样式

    方法1:
        const container = {
            display: flex;
            height: 100%;
        }

        render() {
            return (
                <div style={container}> xxx </div>
            );
        }

    方法2: <div style={{ width: '100px', float: 'left'}}>       // 注意这里属性值要使用引号

组件

一、组件创建

    1、类式组件

        import React, { Component } from 'react'
        import ReactDom, { render } from 'react-dom'

        // 所有创建的组件都继承于Component对象
        class ShowMessage extends Component{
            constructor(props){
                super(props);
            }
            render(){
                return <div>Hello name {this.props.name}</div>
            }
        }
        export default ShowMessage;


    2、函数式组件

        const Welcome = (props) => {
            return <h1>Hello, {props.name}</h1>;
        }
        <Welcome username="this.state.username" />


    函数组件与类式组件的区别:

        类组件有state, 函数组件没有state

        类组件有生命周期函数


二、组件注意的地方

    1、组件名首字母必须大写

    2、根元素只能有一个标签元素

        class ComponentDemo extends Component {
            render(){
                return (
                    <div class="boxA">            {/* 根元素只能有一个 */}
                        aaaaaaaa
                        <div class="boxB">
                            bbbbbb
                        </div>
                    </div>
                )
            }
        }
        export default ComponentDemo;

    3、可以使用es6的 spread 变量传递 {...obj}

        let person = <Person name={window.isLoggedIn ? window.name : ''} />

    4、refs属性获取真实的DOM节点

        组件并不是真实的DOM节点,而是在内存中的一种数据结构叫虚拟DOM,如果要从组件内获取真实的DOM节点,需要用到refs属性

组件嵌套

childList.jsx:

    class ChildrenA extends Component{
        constructor(props){
            super(props);
        }
        render(){
            return (<div>
                {/* 这里是子组件A   this.props.children获取组件的内容,相当前于vue中的solt */}
                {
                    React.Children.map(this.props.children, function(c){        // React.Children是获取组件中的元素
                        return <p>{c}</p>
                    })
                }
            </div>)
        }
    }

    class ParentComponent extends Component{
        render(){
            return <div>
                <ChildrenA>
                    <p>福特</p>
                    <p>丰田</p>
                    <p>本田</p>
                </ChildrenA>
            </div>
        }
    }
    export default ParentComponent;

index.js:

    import ParentComponent from './components/ParentComponent'
    render(<ParentComponent />, docuemnt.querySelector('#box'));

组件间通信

一、组件之间通信的几种情况

    1、父组件向子组件通信

    2、子组件向父组件通信

    3、跨级组件之间通信

    4、非嵌套组件间通信


二、父向子通信 通过props

    parent.jsx
        <child message={this.state.sayMessage} />

    child.jsx
        {this.props.message}        // 接收


三、子向父通信

    parent.jsx
        <child getMessage={this.getMessage.bind(this)} />
        getMessage(msg){
            this.setDate({message: msg})
        }

    child.jsx
        this.props.getMessage('这里告诉父组件消息')


四、跨级组件

    父组件向很深的几层子组件通信有两种方法: 1、props一层一层传   2、使用context对象


五、events非嵌套组件通信, 使用事件订阅

    $ npm i events --save

    1、创建一个ev.js

        import { EventEmitter } from 'events'
        export default new EventEmitter();

    2、brotherA.jsx

        // 在组件装载完成以后
        componentDidMount(){
            // 声明一个自定义事件        
            this.eventEmitter = emitter.addListener("callMe", (msg)=>{
                this.setState({
                    msg
                })
            });
        }

        // 组件销毁前移除事件监听
        componentWillUnmount(){
            emitter.removeListener(this.eventEmitter);
        }

        render(){
            return (
                <div>
                    {this.state.message}
                </div>
            )
        }

    3、brotherB.jsx

        sendBrotherMessage(){
            return emitter.emit("callMe", "Hello");     // callMe为自定义函数,hello为发的消息参数
        }
        <input type="button" value="向A子组件发消息" onClick={this.sendBrotherMessage.bind(this)} />


六、redux或其它状态管理库

Context对象

context对象来跨级传递

context与react版本分两种写法,以下为react v16.3版本的示例

一、React.crateContext() - 创建一个Context

二、初始化createContext下的方法

    1、provider - 父组件中传递的值

    2、consumeer - 子组件接收的值

三、示例:

    创建一个contextStor.js:
        import React, { Component } from 'react'
        export const { Provider, Consumer } = React.createContext("Light");        // 默认主题是Light

    parentPage.jsx:
        import Ca from './cA'
        import { Provider } from './ccontextStor';

        export default class ParnentPage extends Component {
            render () {
                return (
                    <Provider value={{contextVal: '这里是context的传递的内容'}}>
                        <h1>这里是Parent组件</h1>
                        <Ca />
                    </Provider>
                )
            }
        }

    cA.jsx:
        import Cb from './cB'
        export default class Ca extends Component {
            render () {
                return (
                    <div>
                        <h2>这里是CA</h2>
                        <Cb />
                    </div>
                )
            }
        } 

    cB.jsx:
        import { Consumer } from './ccontextStor';
        export default class Cb extends Component {
            render () {
                return (
                    <div>
                        <h3>这里子CB</h3>
                        <Consumer>
                            {
                                context => {
                                    return (
                                        <div>{context.contextVal}</div>
                                    )
                                }
                            }
                        </Consumer>
                    </div>     
                )
            }
        }

数据流

React是单向数据流,数据从父组件传到子组件,子组件通过props获取数据,顶层组件改变了props,React会遍历整个组件树,重新渲染整个组件

数据流包括: Props 和 State

props

{ this.props.name } 读取props的值

一、props API:

    this.props.children - 所有子组件的内容

        <ChildrenComponent>
            <p>aa</p>
            <p>bb</p>
            <p>cc</p>
        </ChildrenComponent>

    this.props.map

    this.props.filter 


二、设置一个默认的props

    /* 通过 defaultProps 来定义 */
    class PropsDemo extends Component {
        constructor(props) {
    super(props);        // 调用父类,如果不写会调不到
       }
        render(){
            return (
                <div class="box">
                    name: {this.props.name}, age: {this.props.age}
                </div>
            )
        }
    }

    // 初始化props
    PropsDemo.defaultProps = {
        name: '123123'        // 默认一个name值,如果父组件没有传值就会取默认值
    }


三、propsType 校验props类型

    $ npm install --save prop-types

    import React, { Component, PropTypes } from 'react';

    class PropTypesDemo extends Component {
        render(){
            return <b>{this.props.title}</b>
        }
    }

    // 静态属性定义propTypes, title只能为string
    PropTypesDemo.propTypes = {
        title: React.PropTypes.string.isRequired
    }

    export default PropTypesDemo;


四、Example

    PropsComponent组件:

        class PropsComponent extends Component {
            render(){
                return (
                    <div class="box">
                        name: {this.props.name}, age: {this.props.age}         {/* 这里接收props值 */}
                    </div>
                )
            }
        }

        // 输出组件接口
        export default PropsComponent;

    index.js:

        import PropsComponent from '../components/props';

        // 要传的值
        let obj = {
            name: 'haha',
            age: '300'
        }

        // 方法1、直接传给属性
        render(<PropsComponent name={obj.name} age={obj.age} />, document.querySelector('body'));

        // ...obj 来对多值进行解析赋值
        render(<PropsComponent {...obj} />, document.querySelector('body'));


五、this.props.children 访问自定义子节点

    // 将组件下的所有子节点通过map获取到,并重新包装
    import React, { Component } from 'react';

    class UseChildren extends Component {
        render(){
            return  <ul>
                {
                    // 会将UseChildren组件下所有的元素取出
                    React.Children.map(this.props.children, function(c){
                        return <li> {c} </li>
                    })
                }
            </ul>
        }
    }

    class ChildrenDemo extends Component {
        render(){
            return (
                <UseChildren>
                    <a href="#">Facebook</a>
                    <a href="#">Google</a>
                    <a href="#">Space</a>
                </UseChildren>
            )
        }
    }
    export default ChildrenDemo;


六、this.props.content 可以传任意结构的JSX结构

    // 父组件通过content传值
    <ContentChildrenComponent content={
        <div>
            <h2>React.js 小书</h2>
            <div>开源、免费、专业、简单</div>
            订阅:<input />
        </div>
    } />

    // 子组件调用
    render() {
        return (
            <div>
                { this.props.content }
            </div>
        );
    }


七、组件传值

    <ChildComponent propsData={this.state.username} />

    <LikeButton wordings={{likedText: '已赞', unlikedText: '赞'}} onClick={() => console.log('Click on like button!')}/>

state

每一个React组件都有一个自己的state对象,与props区别是state只能在当前组件内部使用,props可以将数据传递给子组件

一、初始化state

    class StateDemo extends Component {
        constructor(){
            super();            // 继承Component

            // 这里初始化state
            this.state = { 
                userName: 'siguang' 
            }
        }

        setUserName: function(){
            this.setState({userName: 'lulu'});        // 修改state值
        },

        render(){
            return (
                <div>
                    这里是props获取的值 {this.state.userName};        // 获取state的值
                    <input type="button" onClick={this.setUserName.bind(this)} value="点击修改state" />
                </div>
            )
        }
    }


二、setState中this的指向

    1、<Button type="primary" onClick={ this.addCar.bind(this) }>开始</Button>
         <Button type="primary" onClick={ (e) => this.addCar(e) }>开始</Button>        // 渲染中箭头函数

    2、<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>

    3、addCar = () => {
            this.setState(function(state, porps){
                return {
                    car: state.car + 1
                }
            })
        }
        <Button type="primary" onClick={ this.addCar }>开始</Button>


三、setState()异步的问题

    setState()是异步导致如果直接在下面在取state值还没变过来,所以第二个回调是当state值设置成功后在执行的函数

    this.setState(
        { username: uname }, 
        () => {  console.log(this.state.username); }        // 这里还是为之前的值而不是「sg」
    );

refs操作真实DOM

获取ref: this.refs.xxx

Example:

    class RefsDemo extends Component {
        componentDidMount(){
            // refs直接获取
            const contentB = this.refs.content;
            console.log( contentB.innerHTML );
        }

        render(){
            return(
                <div>
                    <h3>React操作DOM</h3>
                    <p ref="content">这里是DOM元素的内容</p>        // 这里定义ref访问的名
                </div>
            )
        }
    }

mixin

通过mixin可以将组件间共享代码,将两个组件共同的属性、方法存储到mixin对象中

mixin不支持ES6的声明组件方式

ES5写法

    import React from 'react';

    // 抽取出的公用方法
    var SetIntervalMixin = {
        componentWillMount: function() {
            this.intervals = [];
        },
        setInterval: function() {
            this.intervals.push(setInterval.apply(null, arguments));
        },
        componentWillUnmount: function() {
            this.intervals.forEach(clearInterval);
        }
    };

    var MixinDemo = React.createClass({

        // 这里加载mixin
        mixins: [SetIntervalMixin],

        getInitialState: function() {
            return {seconds: 0};
        },

        componentDidMount: function() {
            // Call a method on the mixin
            this.setInterval(this.tick, 1000);
        },

        tick: function() {
            this.setState({seconds: this.state.seconds + 1});
        },

        render: function() {
            return (
                <p>
                    计时器已经运行了:  {this.state.seconds} 秒.
                </p>
            );
        }
    });

    export default MixinDemo;

表单

一、defaultValue 与 value 的区别

    <input type="text" value={this.state.inputValue} />

    如果使用 value来绑定state的值,输入值时不会被改变,使用 defaultValue 值会被改变

    class App extends Component {
        constructor(props) {
            super(props);
            this.state = {
                username: ''
            }
        }
        handleUsername(e){
            const val = e.target.value;
            this.setState({username: val});
        }
        getName(){
            alert(this.state.username)
        }
        render() {
            return (
                <div className="App">
                    <input placeholder="Basic usage" defaultValue={this.state.username} onChange={this.handleUsername.bind(this)} />
                    <button type="primary" onClick={this.getName.bind(this)}>Primary</button>
                </div>
            );
        }
    }

二、react表单组件与html的不同

    1、value、checked: 属性设置值后,用户输入无效

    2、textarea: 的值要设置在value属性

        <textarea name="description" value="This is a description." />

    3、select: value属性可以是数据,不建使用option的selected属性

        <select multiple={true} value={['B', 'C']}>
            <option value="A">Apple</option>
            <option value="B">Banana</option>
            <option value="C">Cranberry</option>
        </select>

    4、radio/checkbox/option 点击后触发 onChange

受控组件

受控组件就是为某个form表单组件添加value属性;非受控组件就是没有添加value属性的组件

一、受控组件

    <input>,<textarea>以及<select>通常保持自己的状态和基于用户输入更新它

    handleChange(event) {
        this.setState({value: event.target.value});
    }
    <input type="text" value={this.state.value} onChange={this.handleChange} />


二、不受控制组件

    this.input = React.createRef();                    // 创建不受控组件
    <input type="text" ref={this.input} />    // 绑定
    this.input.current.value                                // 调用不受控组件

    class NameForm extends React.Component {
        constructor(props) {
            super(props);
            this.handleSubmit = this.handleSubmit.bind(this);

            // 创建不受控组件
            this.input = React.createRef();
        }

        handleSubmit(event) {
            alert('A name was submitted: ' + this.input.current.value);
            event.preventDefault();
        }

        render() {
            return (
                <form onSubmit={this.handleSubmit}>
                    <label>
                        Name:
                        <input type="text" ref={this.input} />
                    </label>
                    <input type="submit" value="Submit" />
                </form>
            );
        }
    }

碎片

render的时候需要有一个父的元素,如果组件嵌套会多出来无用的元素,可以使用<React.Fragment> ... </React.Fragment>

class Columns extends React.Component {
    render() {
        return (
            <div>
                <td>Hello</td>
                <td>World</td>
            </div>
        );
    }
}
class Table extends React.Component {
    render() {
        return (
            <table>
                <tr>
                    <Columns />                    // 输出<div> ... </div>
                </tr>
            </table>
        );
    }
}

可以写成

class Columns extends React.Component {
    render() {
        return (
            <React.Fragment>
                <td>Hello</td>
                <td>World</td>
            </React.Fragment>
        );
    }
}

React脚手架搭建

一、可以使用官网的 create-react-app、ant或自己搭  https://github.com/facebook/create-react-app

二、使用的JSX和ES6所以需要转换,使用版本:

    react: "^15.4.2", react-dom: "^15.4.2", react-router: "^2.0.0"", webpack: "^3.10.0", webpack-dev-server: "^2.11.1"

    react 16以上版本和 react-router 4以上版本会有问题

三、搭建

    $ npm init

    $ npm i react react-dom react-router --save

    $ npm i webpack webpack-dev-server --save

    2、安装插件

    $ npm i babel babel-loader babel-core babel-preset-es2015 babel-preset-react --save

    $ npm i node-sass file-loader url-loader css-loader sass-loader style-loader --save

    $ npm i react-hot-loader prop-types events react-slot --save             // react组件,热更新、检测props类型、事件用来通信包、slot

    $ npm i html-webpack-plugin open-browser-webpack-plugin --save            // 自动引入静态资源到相应的html、自动打开浏览器

    $ touch .babelrc webpack.config.js

    $ mkdir src && cd src && touch index.html app.js

    $ .babelrc 加入

        {
            "presets": ["es2015", "react"]
        }

    $ webpack.config.js

        /**
        * User: siguang
        * Date: 2016/12/28
        * Time: 15:04
        */
        const webpack = require('webpack');
        const path = require('path');
        const HtmlWebpackPlugin = require('html-webpack-plugin');
        const openBrowserWebpackPlugin = require('open-browser-webpack-plugin');

        const basePath = __dirname;
        const appPath = path.resolve(basePath, 'src');
        const buildPath = path.resolve(basePath, 'build');

        module.exports = {
            entry: {
                app: path.resolve(appPath, 'app.js')
            },

            output: {
                path: buildPath,
                filename: '[name].min.js?[hash]',
                // chunkFilename: "[name].min.js?[hash]"
            },

            module: {
                loaders: [

                    // 处理require()引入的css文件,并将代码显示到页面的<style>中
                    { test: /\.css$/, loader: "style-loader!css-loader" },

                    // 将scss文件转成css文件
                    { test: /\.scss$/, loader: 'style!css!sass?sourceMap'},

                    // ?limit=8192  limit设置小于8k的图片转成64位编码,大小8于不会被转码
                    { test: /\.(png|jpg|woff|eot|ttf|svg|gif)$/, loader: 'url-loader?limit=8192'},

                    // ES6 转 ES5
                    {
                        test: /\.jsx?$/,
                        exclude: /node_modules/,
                        loader: 'babel-loader',
                        query: {
                            presets: ['es2015','react']
                        }
                    }
                ]
            },


            plugins: [

                // 压缩打包的文件
                new webpack.optimize.UglifyJsPlugin({
                    compress: {
                        //supresses warnings, usually from module minification
                        warnings: false
                    }
                }),

                // html
                new HtmlWebpackPlugin({
                    // 改变页面的<title>标签的内容
                    title: 'Hello World app',
                    // 模版地址
                    template: path.resolve(appPath, 'index.html'),
                    // 构建后的文件名和目录
                    filename: 'index.html',
                    //chunks这个参数告诉插件要引用entry里面的哪几个入口
                    // chunks:['app'],
                    //要把script插入标签里
                    inject:'body'
                }),

                // 热启动
                new webpack.HotModuleReplacementPlugin(),
                // 自动打开浏览器
                new openBrowserWebpackPlugin({ url: 'http://localhost:3000' })
            ],

            // 查找依赖
            resolve:{

                // require或alias时不需要写后缀
                extensions: [".js", ".jsx", ".css", ".json"],
            },

            // webpack-dev-server 配置
            devServer: {
                port: 3000,                 // 端口
                contentBase: 'build',       // 内容目录
                hot: true,                    // 热刷新
                inline: true
            }
        }`

$ package.json

    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "dev": "webpack-dev-server --progress --profile --colors --hot --inline --history-api-fallback",
        "build": "webpack --progress --profile --colors --config webpack.production.config.js"
    },

$ 运行

    $ npm run dev     生产环境,并打开webpack服务器

    $ webpack        执行打包到build目录线上环境使用

https://segmentfault.com/a/1190000005969488

Diff算法

当组件更新时候,react会创建一个新的虚拟dom树并会和之前的dom树进行比较,这个过程就用到了diff算法

常用插件

https://github.com/enaqx/awesome-react#boilerplates

1、路由 - react-router

2、布局 - react-blocks     http://whoisandie.github.io/react-blocks/

3、拖拽 - react-dnd        https://github.com/react-dnd/react-dnd

4、代码编辑器 - react-codemirror        https://github.com/JedWatson/react-codemirror

5、富文本编辑器 - react-quill react-draft-wysiwyg        https://github.com/jpuri/react-draft-wysiwyg

6、拾色器 - rc-color-picker、react-color        https://github.com/react-component/color-picker        http://casesandberg.github.io/react-color/

7、响应式 - react-responsive、react-media    https://github.com/contra/react-responsive        https://github.com/ReactTraining/react-media

8、复制到剪贴板 - react-copy-to-clipboard       https://github.com/nkbt/react-copy-to-clipboard

9、管理 document head - react-helmet          https://github.com/nfl/react-helmet

10、Font Awesome 图标 - react-fa        https://github.com/andreypopp/react-fa

11、二维码 -  qrcode.react         https://ant.design/docs/react/recommendation-cn

12、不在使用className - styled-components

    http://www.alloyteam.com/2017/05/guide-styled-components/
    https://github.com/styled-components/styled-components

13、react写动画效果 - css3transform-react

    $ npm install css3transform-react
    http://www.alloyteam.com/2016/12/react-animations-difficult-to-write-try-react-transformjs/

react-slot - react中写solt    http://npm.taobao.org/package/react-slot

积累问题

1、jsx中for循环出标签并插入到render中, 或使用map

    class SideBar extends Component {
        constructor(props) {
            super(props);
            this.state ={
                menu: [
                    {
                        path: '/home',
                        name: '主页'
                    },
                    {
                        path: '/parentToChild',
                        name: '将父向子组件传数据'
                    },
                ]
            }
        }

        render() {
            // 定义一个返回菜单html内容的方法
            let renderMenuHtml = () => {
                let homeHtml = [];
                this.state.menu.forEach(function(item, idx, arr){
                    homeHtml.push(<li>
                        <Link to={item.path}>{item.name}</Link>
                    </li>)
                })
                return homeHtml
            }

            // 另一种写法map推荐
            {/*
                var renderMenuHtml = this.state.menu.map(function(c){
                    return(
                        <li>
                            <Link to={item.path}>{item.name}</Link>
                        </li>
                    );
                });
            */}

            return (
                <div className="side-box">
                    <ul>
                        {renderMenuHtml()}        {/* 插入内容 */}
                    </ul>
                </div>
            );
        }
    }


2、使用redux子组件会取不到 this.props.history

    因为redux会将值传给props,所以就需要通过context来获取

    解决方法: 在子组件中
    import PropTypes from 'prop-types'

    子组件名.contextTypes = {
        router: PropTypes.object.isRequired
    }

    console.log( this.context.router.history.push('/') );


3、子组件调用父组件中的方法

    class ComponentClider extends Component {
        constructor(props){
            super(props);
        }

        render(){
            return(
                <div>{ this.props.add(111,555) }</div>
            )
        }
    }

    export default ComponentParent extends Component {
        add(a, b){
            return a + b;
        }

        sub(a, b) {
            return a - b;
        }

        render(){
            return (
                <div>
                    {/* 这里可以父组件中自己的方法直接调用 */}
                    { this.add(100, 200) }
                    { this.sub(200, 10) }

                    <ComponentClider add={(a, b) => this.add(a, b)} sub={(a, b) => this.sub(a, b)}></ComponentClider>
                </div>
            )
        }
    } 

    <!-- 父组件也可以写成 -->
    export default ComponentParent extends Component {
        multiplicative(a, b) {
            return a * b
        }

        add = (a, b) => {
            return a + b;
        }

        sub = (a, b)  =>  {
            return a - b;
        }

        render(){
            const propsFun = {
                add: this.add,
                sub: this.sub
            }

            return (
                <div>
                    {/* 这里可以父组件中自己的方法直接调用 */}
                    { this.multiplicative(100, 200) }

                    <ComponentClider {...propsFun}></ComponentClider>
                </div>
            )
        }
    } 


4、react原生动态添加多个className会报错

    // 下面的引用会报错
    import style from './style.css'
    <div className={style.class1 style.class2}</div>

    // 解决 使用classnames
    $ npm install classnames --save

    import classnames from 'classnames'
    import styles from './index.less';

    const clsString = classNames(styles.submit, className);
    <div className={clsString}></div>

常用插件

1、react-document-title    根据不同的路由改变文档的title
    https://www.jianshu.com/p/07ed93350483

2、react-container-query   媒体查询 响应式组件

3、classnames      react引用多个className会报错问题

Antd UI组件问题收集

1、Table的每页条数  通过pagination属性来控制分页的内容

    <Table pagination={{ pageSize: this.state.queryInfo.pageSize }} rowSelection={rowSelection} columns={columns} dataSource={data}  />

    https://www.cnblogs.com/jenifurs-blog/p/6020737.html

| https://react.docschina.org/docs/hello-world.html
| http://www.css88.com/react/docs/try-react.html
| https://juejin.im/post/5a9410c25188257a61325eda // react 新老context
| https://www.jianshu.com/p/fb915d9c99c4 // 组件通信
| http://huziketang.com/books/react/
| http://blog.csdn.net/liwusen/article/category/6522963
| http://react-china.org/ 中文社区
| https://github.com/BruceCham/react-cli // 全家桶
| http://www.alloyteam.com/2015/04/%E5%89%8D%E6%B2%BF%E6%8A%80%E6%9C%AF%E8%A7%A3%E5%AF%86-virtualdom/ // virturalDOM 虚拟DOM
| https://github.com/gaearon/redux-devtools // redux-devtools
| https://guoyongfeng.github.io/book/
| http://uprogrammer.cn/react-tutorial-cn/
| http://huziketang.com/books/react/lesson1
| https://github.com/hyy1115/react-redux-webpack2
| https://github.com/zhbhun/react-learning/tree/master/boilerplate
| http://blog.csdn.net/u013063153/article/details/52497271 // 事件
| https://github.com/gaearon/babel-plugin-react-transform#transforms // babel-plugin-react-transform 相关插件
| http://www.alloyteam.com/2016/03/using-react-to-write-a-simple-activity-pages-design-of-operating-system-article/
| https://juejin.im/post/5a84682ef265da4e83266cc4 // 源码解析
| https://www.jianshu.com/p/ec7c2bab16cc?utm_campaign=maleskine&utm_content=note&utm_medium=pc_all_hots&utm_source=recommendation // vscode 调试react 通过debugger for chrome