搭环境
装装装
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
目录结构
代码展示
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>
);
}
}