React初探

搭环境

装装装
cnpm init --yes
cnpm install react react-dom babelify babel-preset-react babel-preset-es2015 --save

cnpm install babel babel-core babel-loader --save

cnpm install -g webpack webpack-dev-server

cnpm install webpack webpack-dev-server --save
目录结构
目录结构.png
代码展示

package.json

{
    "name": "05-01",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "",
    "license": "ISC",
    "dependencies": {
        "babel": "^6.23.0",
        "babel-core": "^6.25.0",
        "babel-loader": "^7.1.0",
        "babel-preset-es2015": "^6.14.0",
        "babel-preset-react": "^6.11.1",
        "babelify": "^7.3.0",
        "react": "^15.3.2",
        "react-dom": "^15.3.2",
        "webpack": "^1.13.2",
        "webpack-dev-server": "^1.16.1"
    }
}

webpack.config.js

var webpack = require('webpack');
var path = require('path');

module.exports = {
    context: __dirname + '/src',
    entry: './js/index.js',// 入口文件
    module: {
        loaders: [
            {
                test: /\.js?$/,
                exclude: /(node_modules)/,
                loader: 'babel-loader',
                query: {
                    presets: ['react', 'es2015']
                }
            }
        ]
    },
    output: {// 输出
        path: __dirname + '/src/',
        filename: 'bundle.js'
    }
};

webpack.config.js这个版本增加了编译时的一些设置

var debug = process.env.NODE_ENV !== "production";
var webpack = require('webpack');
var path = require('path');

module.exports = {
    context: path.join(__dirname),
    devtool: debug ? "inline-sourcemap" : null,
    entry: "./src/js/index.js",
    module: {
        loaders: [{
            test: /\.js?$/,
            exclude: /(node_modules)/,
            loader: 'babel-loader',
            query: {
                presets: ['react', 'es2015']
            }
        }]
    },
    output: {
        path: __dirname,
        filename: "./src/bundle.js"
    },
    plugins: debug ? [] : [
        new webpack.optimize.DedupePlugin(),
        new webpack.optimize.OccurenceOrderPlugin(),
        new webpack.optimize.UglifyJsPlugin({ mangle: false, sourcemap: false }),
    ]
};

index.js

var React = require('react');
var ReactDOM = require('react-dom');

ReactDOM.render(
    <h1>hello word</h1>,
    document.getElementById("example")
)

index.html

<div id="example">123</div>
<script src="./src/bundle.js"></script>
编译
webpack
webpack --watch
// 下面要在服务器下运行才有效
webpack-dev-server// 需要加尾巴
webpack-dev-server --contentbase src --inline --hot// 不需要加尾巴

开发工具

Chrome调试插件:React Developer Tools
开发工具:Atom
Sublime中可以用bebel格式化React代码
atom-ternjs
atom-beautify
open-in-browser
emmet
file-icons
highlight-line
highlight-selected

组件的导出和导入

header.js

import React from 'react';
import ReactDOM from 'react-dom';

export default class Header extends React.Component {
    render() {
        return (
            <header>
                <h1>这里是头部</h1>
            </header>
        )
    }
}

index.js

var React = require('react');
var ReactDOM = require('react-dom');

import ComponentHeader from './components/header';

class Index extends React.Component {
    render() {
        return (
            <div>
                <ComponentHeader/>
                <h2>页面的内容</h2>
            </div>
        )
    }
}

ReactDOM.render(
    <Index/>,
    document.getElementById("example")
);

解析HTML

中文转Unicode或
<p dangerouslySetInnerHTML={{__html:html}}></p>

生命周期

componentWillMount
componentDidMount

几个属性

state
// state对于模块来说属于自身属性
import React from "react";

export default class ComponentBody extends React.Component {
    constructor() {
        super();// 调用基类的所有的初始化方法
        this.state = {
            username: "Aaayang",
            age: 25
        };
    }
    render() {
        setTimeout(()=>{
            // 更改state
            this.setState({
                username: "jiangjun",
                age: 30
            });
        }, 4000);
        return (
            <div>
                <p>姓名:{this.state.username}</p>
                <p>年龄:{this.state.age}</p>
            </div>
        )
    }
}
props
// 传递参数时{}也可以不要
<ComponentBody username={'Aaayang'} userid={123456}/>

// props对于模块来说属于外来属性
import React from "react";
export default class ComponentBody extends React.Component {
    render() {
        return (
            <div>
                <p>{this.props.username}</p>
                <p>{this.props.userid}</p>
            </div>
        )
    }
}
改变当前页面的state
import React from "react";

export default class ComponentBody extends React.Component {
    constructor() {
        super();// 调用基类的所有初始化方法
        this.state = {
            username: "Aaayang",
            age: 25
        };
    }
    changeUserInfo() {
        this.setState({
            username: "jiangjun",
            age: 30
        });
    }
    render() {
        return (
            <div>
                <p>{this.state.username}</p>
                <p>{this.state.age}</p>
                <input type="button" value="提交" onClick={this.changeUserInfo.bind(this)} />
            </div>
        )
    }
}
子向父传参

body.js

import React from "react";
import BodyChild from './bodychild';

export default class ComponentBody extends React.Component {
    constructor() {
        super();// 调用基类的所有初始化方法
        this.state = {
            username: "Aaayang"
        };
    }
    changeUserInfo(username) {
        this.setState({
            username: username
        });
    }
    handleChildValueChange(event) {
        this.setState({
            username: event.target.value
        });
    }
    render() {
        return (
            <div>
                <p>{this.state.username}</p>
                <input type="button" value="提交" onClick={this.changeUserInfo.bind(this, "jiangjun")} />
                <BodyChild handleChildValueChange={this.handleChildValueChange.bind(this)}/>
            </div>
        )
    }
}

bodychild.js

import React from "React";

export default class BodyChild extends React.Component {
    render() {
        return(
            <div>
                <p>子页面输入:</p>
                <p>
                    <input type="text" onChange={this.props.handleChildValueChange} {...this.props}/>
                </p>
            </div>
        )
    }
}

props验证

<ComponentBody userid={222}/>

const defaultProps = {
    userid: 1111
};
ComponentBody.propTypes = {
    userid: React.PropTypes.number.isRequired
};
ComponentBody.defaultProps = defaultProps;

操作DOM

// Refs是访问到组件内部DOM节点唯一可靠的方法
import React from "react";
import ReactDOM from 'react-dom';

export default class ComponentBody extends React.Component {
    changeUserInfo(username) {
        // 第一种方式
        // var mySubmitButton = document.getElementById('submitButton');
        // ReactDOM.findDOMNode(mySubmitButton).style.backgroundColor = "red";
        
        // 第二种方式
        this.refs.submitButton.style.backgroundColor = 'red';
    }
    render() {
        return (
            <div>
                <input type="button" ref="submitButton" id="submitButton" value="提交" onClick={this.changeUserInfo.bind(this, "jiangjun")} />
            </div>
        )
    }
}

独立组件间共享Mixins

cnpm install react-mixin@2 --save

body.js

import React from "react";
import ReactDOM from 'react-dom';

import ReactMixin from 'react-mixin';
import MixinLog from './mixins'; 

export default class ComponentBody extends React.Component {
    changeUserInfo() {
        MixinLog.log();
    }
    render() {
        return (
            <div>
                <input type="button" value="提交" onClick={this.changeUserInfo.bind(this)} />
            </div>
        )
    }
}

ReactMixin(ComponentBody.prototype, MixinLog);// 注意调用顺序

mixins.js

const MixinLog = {
    componentDidMount() {// 有自己的生命周期
        console.log('MixinLog componentDidMount');
    },
    log() {
        console.log('abcdef...');
    }
};

export default MixinLog;

样式

行内样式
import React from 'react';

export default class ComponentHeader extends React.Component {
    render() {
        const styleComponentHeader = {
            header: {
                backgroundColor: "pink",
                color: "#333",
                textAlign: "center",
                paddingTop: "15px",
                paddingBottom: "15px"
            }
            // 还可以定义其他样式
        };
        return (
            <header style={styleComponentHeader.header} className="smallFontSize">
                <h1>这里是头部</h1>
            </header>
        )
    }
}
引入样式

index.html

// 缺点:引入的是全局的,有污染
<head>
    <link rel="stylesheet" href="./src/css/style.css">
</head>
<div id="example">fd</div>
<script src="./src/bundle.js"></script>
控制样式
import React from 'react';

export default class ComponentHeader extends React.Component {
    constructor() {
        super();
        this.state = {
            miniHeader: false
        };
    }
    switchHeader() {
        this.setState({
            miniHeader: !this.state.miniHeader
        })
    }
    render() {
        const styleComponentHeader = {
            header: {
                backgroundColor: "pink",
                color: "#333",
                textAlign: "center",
                paddingTop: (this.state.miniHeader) ? "3px" : "15px",
                paddingBottom: (this.state.miniHeader) ? "3px" : "15px"
            }
            // 还可以定义其他样式
        };
        return (
            <header style={styleComponentHeader.header} className="smallFontSize" onClick={this.switchHeader.bind(this)}>
                <h1>这里是头部</h1>
            </header>
        )
    }
}

CSS模块化

安装
http://staxmanade.com/CssToReact/  // CSS转React的工具
// 装好后在对应的webpack.config.js中进行配置
babel-plugin-react-html-attrs// 例如:className可以写class
cnpm install babel-plugin-react-html-attrs style-loader css-loader --save
配置webpack.config.js
var debug = process.env.NODE_ENV !== "production";
var webpack = require('webpack');
var path = require('path');

module.exports = {
    context: path.join(__dirname),
    devtool: debug ? "inline-sourcemap" : null,
    entry: "./src/js/index.js",
    module: {
        loaders: [{
            test: /\.js?$/,
            exclude: /(node_modules)/,
            loader: 'babel-loader',
            query: {
                presets: ['react', 'es2015'],
                plugins: ['react-html-attrs']
            }
        },{
            test: /\.css$/,
            loader: 'style!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'
        }]
    },
    output: {
        path: __dirname,
        filename: "./src/bundle.js"
    },
    plugins: debug ? [] : [
        new webpack.optimize.DedupePlugin(),
        new webpack.optimize.OccurenceOrderPlugin(),
        new webpack.optimize.UglifyJsPlugin({ mangle: false, sourcemap: false }),
    ],
};
使用
import React from 'react';

var footerCss = require("../../css/footer.css");// 导入
// 其他页面是直接使用不到miniFooter样式的
export default class ComponentFooter extends React.Component {
    render() {
        console.log(footerCss);
        return (
            <footer class={footerCss.miniFooter}>
                <h1>这里是页脚</h1>
            </footer>
        )
    }
}

样式框架

Material-UI
ANT DESIGN // 国产来自阿里
安装
cnpm install antd --save
更改配置
// 使用ant design适合的配置
test: /\.css$/, loader: 'style-loader!css-loader'
使用
// 先全局引入
import 'antd/dist/antd.css';
// 再局部使用某一个组件
import { Input } from 'antd';

<Input placeholder="Basic usage" />

react-router

cnpm install react-router --save
import {Router,Route,hashHistory} from 'react-router';
<Router history={hashHistory}>
    {/*localhost:8080/*/}
    <Route component={Index} path="/"></Route>
    <Route component={ComponentList}></Route>
</Router>
嵌套路由

index.js

// 嵌套路由需要专门留出位置进行展示内容
var React = require('react');
var ReactDOM = require('react-dom');

import ComponentHeader from './components/header';
import ComponentBody from './components/body';
import ComponentFooter from './components/footer';

import 'antd/dist/antd.css';

export default class Index extends React.Component {

    render() {
        var oBar = true;
        var componentHeader;
        if(oBar) {
            componentHeader = <ComponentHeader/>;
        }
        else {
            componentHeader = <h1>Aaayang</h1>;
        }
        return (
            <div>
                {componentHeader}
                <ComponentBody userid={222}/>
                <div>{this.props.children}</div>
                <ComponentFooter/>
            </div>
        )
    }
}

root.js

import React from 'react';
import ReactDOM from 'react-dom';
import {Router,Route,hashHistory} from 'react-router';

import Index from './index';
import ComponentList from './components/list';
import ComponentDetails from './components/details';

export default class Root extends React.Component {
    render() {
        return (
            <Router history={hashHistory}>
                <Route component={Index} path="/">
                    <Route component={ComponentDetails} path="details"></Route>
                </Route>

                <Route component={ComponentList} path="list"></Route>
            </Router>
        )
    }
}


ReactDOM.render(
    <Root/>,
    document.getElementById("example")
);
路由间跳转
import {Link} from 'react-router';

<ul>
    <li>
        <Link to={`/details`}>详情页</Link>
    </li>
    <li>
        <Link to={`/`}>首页</Link>
    </li>
    <li>
        <Link to={`/list`}>列表</Link>
    </li>
</ul>
// 非Link形式跳转
browserHistory.push(`/`)
路由传参
<Link to={`/list/1234`}>列表</Link>

<Route component={ComponentList} path="list/:id"></Route>

<h2>这里是列表的内容{this.props.params.id}</h2>

JSPM&React

jspm install react@0.14.0-rc1
jspm install react-dom@0.14.0-rc1
jspm install semantic-ui
jspm install css
browser-sync start --server --no-notify --files 'index.html, app/**/*.js'
atom ./

React Mark

class Index extends React.Component {
    render() {
        var component;
        var temp = "header";
        if(temp == "header") {
            component = <ComponentHeader/>;// 可以给变量直接赋值一个完整的组件
        }
        else {
            component = <ComponentFooter/>;
        }
        return (
            <div>
                {component}
            </div>
        );
    }
}
// React也可以直接导出一个无名的Class
export default React.createClass({
    render() {
        return (
            <div>about</div>
        )
    }
})
// 替换ReactDOM.render
import React from 'react'
import { render } from 'react-dom'
import App from './modules/App'
import About from './modules/About';
import Repos from './modules/Repos';

import {Router, Route, hashHistory} from 'react-router';

render(
    <Router history={hashHistory}>
        <Route path="/" component={App}/>
        <Route path="/about" component={About}/>
        <Route path="/repos" component={Repos}/>
    </Router>, 
    document.getElementById('app')
);
// 组件的正确姿势
import React from 'react'
import { render } from 'react-dom'
import App from './modules/App'
import About from './modules/About';
import Repos from './modules/Repos';

import {Router, Route, hashHistory, Link} from 'react-router';

render(
    <Router history={hashHistory}>
        <Route path="/" component={App}/>
        <Route path="/about" component={About}/>
        <Route path="/repos" component={Repos}/>
    </Router>, 
    document.getElementById('app')
);
// getDefaultProps()
import React from 'react';
import ReactDOM from 'react-dom';

const Item = React.createClass({
    getDefaultProps() {
        return {
            name: "Aaayang"
        }
    },
    render() {
        return (
            <div>{this.props.name}</div>
        );
    }
});

ReactDOM.render(
    <div>
        <Item name="Bbb"/>
    </div>,
    document.getElementById("app")
)
定义class的两种方式
// mixins
import React from 'react';
import ReactDOM from 'react-dom';

const exf = {
    test() {
        console.log(this.props.name, 233333);
    }
};

const Item = React.createClass({
    getDefaultProps() {
        return {
            name: "Aaayang"
        }
    },
    getInitialState() {
        return {
            result: 23333
        }
    },
    mixins: [exf],
    // 相当于test(),
    render() {
        return (
            <div>
                <button onClick={this.test}>click me</button>
                <div>{this.props.name}</div>
                <div>{this.state.result}</div>
            </div>
        );
    }
});

ReactDOM.render(
    <div>
        <Item name="Bbb"/>
    </div>,
    document.getElementById("app")
)
// ES6的正确姿势
import React from 'react';
import ReactDOM from 'react-dom';

class Item extends React.Component {
    constructor(props) {
        super(props);
        // 下面等价于getInitialState
        this.state = {
            result: 23333333
        }
    }
    static defaultProps() {
        return {
            name: "Aaayang"
        }
    }
    // mixins es6不支持
    test() {
        console.log(this.props.name);
    }
    render() {
        return (
            <div>
                <button onClick={this.test.bind(this)}>点击</button>
                <div>{this.props.name}</div>
                <div>{this.state.result}</div>
            </div>
        );
    }
}

ReactDOM.render(
    <div>
        <Item name="Bbb"/>
    </div>,
    document.getElementById("app")
)
生命周期
init: 初始化
    getDefaultProps
    getInitialState
mount: 装载
    will
    render...
    did
update: 有可能调用多次
    will
    did
unmount
    will
// 关于getInitialState和getDefaultProps
import React from 'react';
import ReactDOM from 'react-dom';

const Item = React.createClass({
    displayName: 'Item',
    getDefaultProps() {
        // 只运行一次
        console.log("getDefaultProps");
        return {
            name: "Aaayang"
        }
    },
    getInitialState() {
        // 有多少个Item组件运行多少次
        console.log("getInitialState");
        return {
            age: 18
        }
    },
    render() {
        return (
            <div>
                <div>{this.props.name}</div>
                <div>{this.state.age}</div>
            </div>
        );
    }
});

ReactDOM.render(
    <div>
        <Item/>
        <Item/>
        <Item/>
    </div>,
    document.getElementById("app")
)
// 几个重要的时机
import React from 'react';
import ReactDOM from 'react-dom';

const Item = React.createClass({
    displayName: 'Item',
    getDefaultProps() {
        // 只运行一次
        console.log("getDefaultProps");
        return {
            name: "Aaayang"
        }
    },
    getInitialState() {
        // 有多少个Item组件运行多少次
        console.log("getInitialState");
        return {
            age: 18
        }
    },
    // 即将加载
    componentWillMount() {
        console.log("componentWillMount");
        // 有更改内部状态的机会...
        this.state.age = 38;
    },
    // 加载完毕
    componentDidMount() {
        console.log("componentDidMount");
        const dom = ReactDOM.findDOMNode(this);
        // console.log(dom);
        // 有获取原生DOM的机会
        // 和其他框架配合的机会
        // 获取后端数据的机会
        let isYellow = false;
        setInterval(function() {
            if(isYellow) {
                dom.style.backgroundColor = "red";
                isYellow = false;
            }
            else {
                dom.style.backgroundColor = "yellow";
                isYellow = true;
            }
            
        }, 3000);
    },
    render() {
        return (
            <div>
                <div>{this.props.name}</div>
                <div>{this.state.age}</div>
            </div>
        );
    }
});

ReactDOM.render(
    <div>
        <Item/>
    </div>,
    document.getElementById("app")
)
探索生命周期
import React from 'react';
import ReactDOM from 'react-dom';

const Item = React.createClass({
    displayName: 'Item',
    getDefaultProps() {
        // 只运行一次
        console.log("getDefaultProps");
        return {
            name: "Aaayang"
        }
    },
    getInitialState() {
        // 有多少个Item组件运行多少次
        console.log("getInitialState");
        return {
            age: 18
        }
    },
    // 即将加载
    componentWillMount() {
        console.log("componentWillMount");
        // 有更改内部状态的机会
        this.state.age = 38;
    },
    // 加载完毕
    componentDidMount() {
        console.log("componentDidMount");
        const dom = ReactDOM.findDOMNode(this);
        console.log(dom);
        // 有获取原生DOM的机会
        // 和其他框架配合的机会
        // 获取后端数据的机会
        let isYellow = false;
        this.state.loopNum = setInterval(function() {
            if(isYellow) {
                dom.style.backgroundColor = "red";
                isYellow = false;
            }
            else {
                dom.style.backgroundColor = "yellow";
                isYellow = true;
            }
            
        }, 3000);
    },
    componentWillReceiveProps(nextProps) {
        // 外部注入props参数时执行,重新render时也执行
        console.log("componentWillReceiveProps");
        // console.log(nextProps.comments);// 若传过来一个comments可以这样获取
    },
    shouldComponentUpdate(nextProps, nextState) {
        // 有机会判断新的props和state是否真的改变了
        console.log("shouldComponentUpdate");
        // 强制刷新时会忽略shouldComponentUpdate,直接执行下面的update
        // setState和外部重新渲染时都会触发shouldComponentUpdate,下面返回false时不会执行update,返回true会执行
        return false;
    },
    // 下面update会根据shouldComponentUpdate的返回值来决定是否执行,真就执行,假不执行,forceUpdate会绕过shouldComponentUpdate
    // 外部重新渲染时会被调用、内部状态改变、forceUpdate
    componentWillUpdate(nextProps, nextState) {
        // 内部不要调用setState、forceUpdate会死循环
        // 新的props和state
        console.log("componentWillUpdate");
    },
    componentDidUpdate(oldProps, oldState) {
        // 旧的props和state
        console.log("componentDidUpdate");
    },
    update() {
       // 内部的状态更新时也会调用componentWillUpdate和componentDidUpdate
       this.setState({
           name: "yangkang"
       });
        // 强制渲染时也会
        // this.forceUpdate();
    },
    render() {
        console.log("render");
        return (
            <div>
                {/* ES6的写法才需要bind(this) */}
                <button onClick={this.update}>update</button>
                <div>{this.props.name}</div>
                <div>{this.state.age}</div>
            </div>
        );
    },
    componentWillUnmount() {
        console.log("componentWillUnmount");
        clearInterval(this.state.loopNum);
    }
});

function render(bool) {
    ReactDOM.render(
        <div>
            <Item/>
            {bool ? <Item/> : ""}
        </div>,
        document.getElementById("app")
    )
}


render(true);

// render();// 模拟外部调用重新渲染

document.getElementById("clear").onclick = function(){
    render();
};
表单属性
import React from 'react';
import ReactDOM from 'react-dom';

const Form = React.createClass({
    getInitialState() {
        return {
            inputValue: "Aaayang"
        }
    },
    changeHandle(event) {
        // this.state.inputValue = event.target.value;
        // this.forceUpdate();
        this.setState({
            inputValue: event.target.value
        });
    },
    render() {
        return <form>
            <input type="text" onChange={this.changeHandle} value={this.state.inputValue}/>
        </form>
    }
});

ReactDOM.render(
    <Form/>,
    document.getElementById("app")
);
refs
// 点击获取焦点
import React from 'react';
import ReactDOM from 'react-dom';

const Form = React.createClass({
    click() {
        this.refs.myinput.focus();
    },
    render() {
        return <div>
            <input type="text" ref="myinput"/>
            <button onClick={this.click}>click me</button>
        </div>
    }
});

ReactDOM.render(
    <Form/>,
    document.getElementById("app")
);
// 自动获取焦点
import React from 'react';
import ReactDOM from 'react-dom';

const Form = React.createClass({
    render() {
        return <div>
            {/*这里会先于componentDidMount执行*/}
            <input type="text" ref={function(dom){dom.focus()}}/>
        </div>
    }
});

ReactDOM.render(
    <Form/>,
    document.getElementById("app")
);
// 另一种写法
import React from 'react';
import ReactDOM from 'react-dom';

import Clock from './modules/Clock';

class Index extends React.Component {
    componentDidMount () {
        this.input.focus()
    }
    render() {
        return (
            <div>
            {/* <input ref={(dom) => dom.focus()}/> */}
            <input ref={(dom) => this.input = dom} />
            </div>
        );
    }
}

ReactDOM.render(
    <Index/>,
    document.getElementById("app")
)
// 结合生命周期自动获取焦点
import React from 'react';
import ReactDOM from 'react-dom';

const Form = React.createClass({
    componentDidMount() {
       this.refs.myinput.focus();
    },
    render() {
        return <div>
            <input type="text" ref="myinput"/>
            <button>click</button>
        </div>
    }
});

ReactDOM.render(
    <Form/>,
    document.getElementById("app")
);
事件
import React from 'react';
import ReactDOM from 'react-dom';

const Comp = React.createClass({
    getInitialState() {
        return {
            top: 0,
            left: 0
        }
    },
    keyup(event) {
        if(event.keyCode == 38) {
            this.setState({
                top: this.state.top + 10
            })
        }
    },
    render() {
        return <div ref={dom=>{if(dom)dom.focus()}} style={{width: "100px", height: "100px", border: "1px solid #333",position: "absolute",top: this.state.top + "px"}} tabIndex={1} onKeyUp={this.keyup}>

        </div>
    }
});

ReactDOM.render(
    <Comp/>,
    document.getElementById("app")
)
组件间通信
// 父传子
import React from 'react';
import ReactDOM from 'react-dom';

const Item = React.createClass({
    render() {
        let style = this.props.actived ? {backgroundColor: "red"} : {};
        return <li style={style}>{this.props.name}</li>
    }
});

const Comp = React.createClass({
    getInitialState() {
        return {
            list: []
        }
    },
    // 即将装载
    componentWillMount() {
        this.state.list = this.props.data.map(item=>{
            return {
                name: item,
                actived: false
            }
        });
    },
    componentDidMount() {
        setTimeout(()=>{
            this.state.list[1].actived = true;
            this.forceUpdate();
        },3000);
    },
    render() {
        return <ul>
                {this.state.list.map((item,index)=><Item actived={item.actived} name={item.name} key={index}/>)}  
            </ul>
    }
});

const list = ["aaa", "bbb", "ccc", "ddd"];
ReactDOM.render(
    <Comp data={list}/>,
    document.getElementById("app")
)
// 子传父,子调父函数
import React from 'react';
import ReactDOM from 'react-dom';

const Item = React.createClass({
    render() {
        let style = this.props.actived ? {backgroundColor: "red"} : {};
        return <li onClick={this.props.callback} style={style}>{this.props.name}</li>
    }
});

const Comp = React.createClass({
    getInitialState() {
        return {
            list: []
        }
    },
    // 即将装载
    componentWillMount() {
        this.state.list = this.props.data.map(item=>{
            return {
                name: item,
                actived: false
            }
        });
    },
    componentDidMount() {
        setTimeout(()=>{
            this.state.list[1].actived = true;
            this.forceUpdate();
        },3000);
    },
    callback(item) {
        console.log(item.name);
    },
    render() {
        return <ul>
                {this.state.list.map((item,index)=><Item callback={this.callback.bind(this,item)} actived={item.actived} name={item.name} key={index}/>)}  
            </ul>
    }
});

const list = ["aaa", "bbb", "ccc", "ddd"];
ReactDOM.render(
    <Comp data={list}/>,
    document.getElementById("app")
)
// 事件总线程通信
eventemitter
import React from 'react';
import ReactDOM from 'react-dom';
import EventEmitter from 'eventemitter';

// 事件总线
const eventbus = new EventEmitter.EventEmitter();

 

const Item = React.createClass({
    render() {
        let style = this.props.actived ? {backgroundColor: "red"} : {};
        return <li onClick={this.props.callback} style={style}>{this.props.name}</li>
    }
});

const Comp = React.createClass({
    getInitialState() {
        return {
            list: []
        }
    },
    // 即将装载
    componentWillMount() {
        // 找不到this
        // this.props.bus.on('test event', function() {
        //     console.log('handle is ' + this.props.name);
        // });
        this.props.bus.on('test event', () => {
            console.log('handle is ' + this.props.name);
        });
        this.state.list = this.props.data.map(item=>{
            return {
                name: item,
                actived: false
            }
        });
    },
    componentDidMount() {
        setTimeout(()=>{
            this.state.list[1].actived = true;
            this.forceUpdate();
        },3000);
    },
    callback(item) {
        console.log(item.name);
    },
    render() {
        return <ul>
                {this.state.list.map((item,index)=><Item callback={this.callback.bind(this,item)} actived={item.actived} name={item.name} key={index}/>)}  
            </ul>
    }
});

const list = ["aaa", "bbb", "ccc", "ddd"];
ReactDOM.render(
    <div>
        <Comp data={list} bus={eventbus} name="comp one"/>
        <Comp data={list} bus={eventbus} name="comp two"/>
    </div>,
    document.getElementById("app")
)

setTimeout(function() {
    eventbus.emit('test event');
},2e3);
Props vs State
// 尽量用无状态的props
// 最小化state
// props vs state 应该放在render方法中

state.size = props.data.length; // 不推荐,因为可以在render中直接用props.data.length

// 下面也不推荐,不要把计算的结果保存在state,可以存在render的变量中
state.len = 12;
props.size = 5;
state.result = state.len * props.size;
实用工具
// Animation
// react-with-addons
组件树
Flux架构

React小书

判断的3种形式
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Header extends Component {
    render() {
        const isGood = true;
        return (
            <div>
                <h1>React消暑{isGood ? <strong>is good</strong> : <strong>is bad</strong>}</h1>
            </div>
        )
    }
}

ReactDOM.render(
    <Header/>,
    document.getElementById("app")
)
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Header extends Component {
    render() {
        const isGood = true;
        const goodWord = <strong>is good</strong>;
        const badWord = <strong>is bad</strong>;
        return (
            <div>
                <h1>React消暑{isGood ? goodWord : badWord}</h1>
            </div>
        )
    }
}

ReactDOM.render(
    <Header/>,
    document.getElementById("app")
)
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Header extends Component {
    renderGoodWord(goodWord, badWord) {
        const isGoodWord = true;
        return isGoodWord ? goodWord : badWord;
    }
    render() {
        const isGood = true;
        return (
            <div>
                <h1>React消暑
                    {this.renderGoodWord(<strong>is good</strong>,<span>is not good</span>)}
                </h1>
            </div>
        )
    }
}

ReactDOM.render(
    <Header/>,
    document.getElementById("app")
)
事件监听
on* 的事件监听只能用在普通的 HTML 的标签上,而不能用在组件标签上
this/传参/e
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Header extends Component {
    constructor() {
        // 套路2,super必备
        super();
        this.state = {
            text: ""
        }
    }
    handleClick(text,e) {
        this.setState({
            text: text
        });
        // 套路3:注意
        console.log(this.state.text, 23333);// 当你调用 setState 的时候,React.js 并不会马上修改 state
        console.log(this,text,e.target.innerHTML);// 获取this的正确姿势,传参,e
    }
    render() {
        // 套路1
        const { text } = this.state;
        return (
            <div onClick={this.handleClick.bind(this,"world")}>
                hello {text}
            </div>
        )
    }
}
ReactDOM.render(
    <Header/>,
    document.getElementById("app")
)
setState注意
// 当你调用 setState 的时候,React.js 并不会马上修改 state,下面是解决套路
// 下面我们进行了三次 setState,但是实际上组件只会重新渲染一次,而不是三次;这是因为在 React.js 内部会把 JavaScript 事件循环中的消息队列的同一个消息中的 setState 都进行合并以后再重新渲染组件
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Header extends Component {
    constructor() {
        super();
        this.state = {
            count: 0
        }
    }
    onHandleClick() {
        this.setState((prevState) => {
            // 返回一个对象作为更新 state 的对象
            return {
                count: 1
            }
        })
        this.setState((prevState) => {
            return {
                count: prevState.count + 1
            }
        })
        this.setState((prevState) => {
            return {
                count: prevState.count + 1
            }
        })
    }
    render() {
        console.log("我点击按钮的时候虽然调用了3次setState,但只渲染了一次喔");
        const { count } = this.state;
        return <div onClick={this.onHandleClick.bind(this)}>
            hello {count}
        </div>
    }
}
ReactDOM.render(
    <Header/>,
    document.getElementById("app")
)
this.props
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Header extends Component {
    render() {
        const text = this.props.words || {
            text1: "hello",
            text2: "Aaayang"
        };
        return <div>
            {text.text1} {text.text2}
        </div>
    }
}
ReactDOM.render(
    <Header words={{text1: "hello", text2: "world"}}/>,
    document.getElementById("app")
)
// defaultProps
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Header extends Component {
    constructor(props) {
        super(props)
    }
    render() {
        return <div>
            {console.log(this.props.name)}
            {this.props.name}
        </div>
    }
}
Header.defaultProps = {
    name: "Aaayang"
};
ReactDOM.render(
    <Header/>,
    document.getElementById("app")
)
// 一旦传过来的props不可变
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

// 可以主动地通过重新渲染的方式把新的 props 传入组件当中,这样这个组件中由 props 决定的显示形态也会得到相应的改变
// 组件内部无法控制也无法修改。除非外部组件主动传入新的 props,否则组件的 props 永远保持不变
// state 是让组件控制自己的状态,props 是让外部对组件自己进行配置
class Header extends Component {
    constructor() {
        super();
        this.state = {
            name: "Aaayang"
        };
    }
    changeName() {
        this.setState({
            name: "Bbb"
        })
    }
    render() {
        // 套路二:但我可以通过传的时候改变呀
        return <div onClick={this.changeName.bind(this)}>
            <Con name={this.state.name}/>
        </div>
    }
}
class Con extends Component {
    render() {
        // 套路一:一旦被传过来,props是不可改变
        return (
            <div>my name is {this.props.name}</div>
        );
    }
}
ReactDOM.render(
    <Header/>,
    document.getElementById("app")
)
函数式组件
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

// 因为不鼓励使用state,0.14版本后引入了无状态组件:函数式组件
const HelloWorld = (props) => {
    const sayHi = (event) => console.log("hello world")
    return (
        <div onClick={sayHi}>Hello world</div>
    )
}
ReactDOM.render(
    <HelloWorld/>,
    document.getElementById("app")
)
循环列表套路
// 基础
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

const users = [
    { username: 'Jerry', age: 21, gender: 'male' },
    { username: 'Tomy', age: 22, gender: 'male' },
    { username: 'Lily', age: 19, gender: 'female' },
    { username: 'Lucy', age: 20, gender: 'female' }
];

class Index extends Component {
    render() {
        const usersElements = users.map((value, index) => {
            return <div key={index}>
                <span>姓名:{value.username}</span>
                <span>年龄:{value.age}</span>
                <span>性别:{value.gender}</span>
                <hr/>
            </div>
        });
        return (
            <div>
                {usersElements}
            </div>
        );
    }
}
ReactDOM.render(
    <Index/>,
    document.getElementById("app")
)
// 进化1
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

const users = [
    { username: 'Jerry', age: 21, gender: 'male' },
    { username: 'Tomy', age: 22, gender: 'male' },
    { username: 'Lily', age: 19, gender: 'female' },
    { username: 'Lucy', age: 20, gender: 'female' }
];

class Index extends Component {
    render() {
        return (
            <div>
                {users.map((value, index) => {
                    return <div key={index}>
                        <span>姓名:{value.username}</span>
                        <span>年龄:{value.age}</span>
                        <span>性别:{value.gender}</span>
                        <hr/>
                    </div>
                })}
            </div>
        );
    }
}
ReactDOM.render(
    <Index/>,
    document.getElementById("app")
)
// 进化2
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

const users = [
    { username: 'Jerry', age: 21, gender: 'male' },
    { username: 'Tomy', age: 22, gender: 'male' },
    { username: 'Lily', age: 19, gender: 'female' },
    { username: 'Lucy', age: 20, gender: 'female' }
];

class Index extends Component {
    render() {
        return (
            <div>
                {users.map((value, index) =>
                    <div key={index}>
                        <span>姓名:{value.username}</span>
                        <span>年龄:{value.age}</span>
                        <span>性别:{value.gender}</span>
                        <hr/>
                    </div>
                )}
            </div>
        );
    }
}
ReactDOM.render(
    <Index/>,
    document.getElementById("app")
)
// 进化3
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

const users = [
    { username: 'Jerry', age: 21, gender: 'male' },
    { username: 'Tomy', age: 22, gender: 'male' },
    { username: 'Lily', age: 19, gender: 'female' },
    { username: 'Lucy', age: 20, gender: 'female' }
];

class User extends Component {
    render() {
        const { user } = this.props;
        return (
            <div>
                <span>姓名:{user.username}</span>
                <span>年龄:{user.age}</span>
                <span>性别:{user.gender}</span>
                <hr/>
            </div>
        );
    }
}
ReactDOM.render(
    <div>
        {users.map((user, index) => <User user={user} key={index}/>)}
    </div>,
    document.getElementById("app")
)
生命周期

class Header extends Component {
    constructor() {
        super()
        // step1
        console.log('construct')
    }

    componentWillMount() {
        // step2
        console.log('component will mount')
    }

    componentDidMount() {
        // step4
        console.log('component did mount')
    }

    render() {
        // step3
        console.log('render')
        return (
            <div>
                <h1 className='title'>React 小书</h1>
            </div>
        )
    }
}
-> constructor

-> componentWillMount

-> render

-> componentDidMount

-> componentWillUnmount
关于销毁
import React from 'react';
import ReactDOM from 'react-dom';

import Clock from './modules/Clock';

class Index extends React.Component {
    constructor() {
        super();
        this.state = {
            isShowClock: true
        }
    }
    // Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the undefined component.
    handleShowOrHide () {
        this.setState({
            isShowClock: !this.state.isShowClock
        })
    }
    // 多次的隐藏和显示会让 React.js 重新构造和销毁 Clock 组件
    // 每次构造都会重新构建一个定时器
    // 而销毁组件的时候没有清除定时器,所以你看到报错会越来越多。而且因为 JavaScript 的闭包特性,这样会导致严重的内存泄漏
    render() {
        return (
            <div>
                {
                    this.state.isShowClock
                    ?
                    <Clock />
                    :
                    null
                }
                
                <button onClick={this.handleShowOrHide.bind(this)}>hi</button>
            </div>
        );
    }
}

ReactDOM.render(
    <Index/>,
    document.getElementById("app")
)
// clock
import React from 'react';

export default class Clock extends React.Component {
    constructor() {
        super()
        this.state = {
            date: new Date()
        }
    }
    componentWillMount() {
        this.timer = setInterval( () => {
            this.setState({
                date: new Date()
            })
        }, 1000 );
    }
    componentWillUnmount() {
        clearInterval(this.timer);
    }
    render() {
        return (
            <div>
                <p>现在的时间是:</p>
                {this.state.date.toLocaleTimeString()}
            </div>
        );
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容