此文章为阮一峰大神写的关于react的DEMOreact的DEMO, 建议看英文版
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
此项目是一个针对reactJS 简单DEMO的集合.
有道是大繁若简, 在没有样式的前提下, DEMO被写出来; 同样, 你会发现, 在跟随这些DEMO学习react的过程中, 基本上没有什么难点.
Related Projects
- Flux Demo
- Webpack Demos
- React Router Tutorial
- CSS Modules Demos
- React Testing Demo
- A boilerplate for React-Babel-Webpack project
如何使用
首先用git把项目拷贝到你的磁盘上
$ git clone git@github.com:ruanyf/react-demos.git
接下来就可以运行你的demo啦
HTML Template
<!DOCTYPE html>
<html>
<head>
<script src="../build/react.js"></script>
<script src="../build/react-dom.js"></script>
<script src="../build/browser.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
// ** Our code goes here! **
</script>
</body>
</html>
Demo01: Render JSX
React中的模板语法是继承自 JSX. 在JSX语法中, 将HTML标签直接写到JS代码中, 这是被允许的. ReactDOM.render()
是一个可以将JSX语法转化为HTML标签的方法, 并且在特定的DOM节点中渲染出来.
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
需要注意的是: 你必须要用 <script type="text/babel">
来指明你写的是JSX语法的代码, 除此之外你还要引入 browser.min.js
, 它是Babel的 browser version , 能够在浏览器中真正地呈现这种转化.
在v0.14以前, React用 JSTransform.js
来转换 <script type="text/jsx">
语法. 不过, 它早已经被摈弃了 (更多信息).
Demo02: Use JavaScript in JSX
你也可以在JSX使用原生JavaScript. JSX是把三角括号 (<) 作为HTML语法的开始符, 并且把花括号 ({
) 当做JS语法的开始符.
var names = ['Alice', 'Emily', 'Kate'];
ReactDOM.render(
<div>
{
names.map(function (name) {
return <div>Hello, {name}!</div>
})
}
</div>,
document.getElementById('example')
);
Demo03: Use array in JSX
如果有一个JS变量是数组, JSX语法会遍历截取该数组中的所有成员.
var arr = [
<h1>Hello world!</h1>,
<h2>React is awesome</h2>,
];
ReactDOM.render(
<div>{arr}</div>,
document.getElementById('example')
);
Demo04: Define a component
React.createClass()
创建一个组件类, 这个组件类执行一个渲染方法, 这个渲染方法是返回这个类的实例化组件. 获取一个实例化对象, 不需要使用 new
, 仅仅把它当做一个普通的HTML标签即可.
var HelloMessage = React.createClass({
render: function() {
return <h1>Hello {this.props.name}</h1>;
}
});
ReactDOM.render(
<HelloMessage name="John" />,
document.getElementById('example')
);
组件也会有属性, 并且可以通过 this.props.[attribute]
来获取到这些属性, 就像 <HelloMessage name="John" />
的 this.props.name
是 John.
请记住: 组件名字的首字母必须大写, 否则React就会抛出错误. 例如: HelloMessage
是可以作为一个组建的名字, 但是 helloMessage
不行. 除此之外, 一个React组件只能有一个直属子节点(据我猜想, 应该是JSX语法只能解析到第一个HTML标签的结束符, 自此终止).
// wrong
var HelloMessage = React.createClass({
render: function() {
return <h1>
Hello {this.props.name}
</h1><p>
some text
</p>;
}
});
// correct
var HelloMessage = React.createClass({
render: function() {
return <div>
<h1>Hello {this.props.name}</h1>
<p>some text</p>
</div>;
}
});
Demo05: this.props.children
React 使用 this.props.children
方法来获取组件的孩子节点.
var NotesList = React.createClass({
render: function() {
return (
<ol>
{
React.Children.map(this.props.children, function (child) {
return <li>{child}</li>;
})
}
</ol>
);
}
});
ReactDOM.render(
<NotesList>
<span>hello</span>
<span>world</span>
</NotesList>,
document.getElementById('example')
);
注意下, this.props.children
的值有三种可能结果.
- 如果组件没有孩子节点, 该值为
undefined
; - 如果只有一个孩子节点, 该值为
object
; - 如果有多个孩子节点,
array
.
所以在操作这个方法时, 一定要小心.
React 也给了我们一个万用方法 React.Children
, 用来处理 this.props.children
方法奇怪的数据结构. 你可以用 React.Children.map
来迭代 this.props.children
而不用担心他的数据类型到底是undefined
还是 object
. 查看官方文档 获取更多与React.Children
相关的提示.
Demo06: PropTypes
在React中, 组件有许多被叫做”props”的属性, 这些属性可以是任何类型.
有时候你需要使用一种方式来校验这些”props”. 此外, 你也并不想要用户在你的组件随意输入一些值.
React 就有一种专门针对于此的解决方案, 它就是”PropTypes”.
var MyTitle = React.createClass({
propTypes: {
title: React.PropTypes.string.isRequired,
},
render: function() {
return <h1> {this.props.title} </h1>;
}
});
组件MyTitle
上有一个叫title
的props. PropTypes 告诉 React 该组件的title是必填的, 并且它的值必须是一个string类型的.
如果我们尝试把title
的值给成数值类型的, 如下:
var data = 123;
ReactDOM.render(
<MyTitle title={data} />,
document.getElementById('example')
);
那么, 这个props无法通过校验, 并且console面板会给你一个错误提示.
Warning: Failed propType: Invalid prop `title` of type `number` supplied to `MyTitle`, expected `string`.
看看 官方文档 寻求更多关于PropTypes的解释.
P.S. 如果你想给props一个默认值, 请使用getDefaultProps()
这个方法.
var MyTitle = React.createClass({
getDefaultProps : function () {
return {
title : 'Hello World'
};
},
render: function() {
return <h1> {this.props.title} </h1>;
}
});
ReactDOM.render(
<MyTitle />,
document.getElementById('example')
);
Demo07: Finding a DOM node
有时你需要在组件中引用一个DOM节点. 而React则给出了 ref
属性以便于查找节点.
var MyComponent = React.createClass({
handleClick: function() {
this.refs.myTextInput.focus();
},
render: function() {
return (
<div>
<input type="text" ref="myTextInput" />
<input type="button" value="Focus the text input" onClick={this.handleClick} />
</div>
);
}
});
ReactDOM.render(
<MyComponent />,
document.getElementById('example')
);
被联想的DOM节点应该有一个ref
属性, 并且 this.refs.[refName]
会返回对应的DOM节点. 请注意: 只有在这个组件嵌入到DOM后, 你才能做这些事情, 否则只能得到 null
.
Demo08: this.state
React 把组件看成状态机, 并且使用 this.state
来获取组件状态, getInitialState()
这个方法就是用来初始化 this.state
(初始化操作在组件绑定到DOM之前就被执行了), this.setState()
这个方法是用来更新 this.state
并且重新渲染组件.
var LikeButton = React.createClass({
getInitialState: function() {
return {liked: false};
},
handleClick: function(event) {
this.setState({liked: !this.state.liked});
},
render: function() {
var text = this.state.liked ? 'like' : 'haven\'t liked';
return (
<p onClick={this.handleClick}>
You {text} this. Click to toggle.
</p>
);
}
});
ReactDOM.render(
<LikeButton />,
document.getElementById('example')
);
你也可以使用组件属性去注册事件函数, 就像 onClick
, onKeyDown
, onCopy
等等. 官方文档有所有的支持事件.
Demo09: Form
根据React的设计理念, this.state
不仅描绘了组件的状态, 还会随着人机交互而改变组件; 同时, this.props
也陈述了组件的特性是稳定且不易改变的.
因此, Since that, 表单组件的属性值, 例如 <input>, <textarea>, and <option> 等等, 都是不会被任何用户输入所改变. 作为对用户输入的反馈, 如果想获取甚至对属性值进行更改, 你就要用到onChange事件.
var Input = React.createClass({
getInitialState: function() {
return {value: 'Hello!'};
},
handleChange: function(event) {
this.setState({value: event.target.value});
},
render: function () {
var value = this.state.value;
return (
<div>
<input type="text" value={value} onChange={this.handleChange} />
<p>{value}</p>
</div>
);
}
});
ReactDOM.render(<Input/>, document.getElementById('example'));
更多信息请参考官方文档.
Demo10: Component Lifecycle
组件生命周期分为三个部分:
- 绑定中(插入到DOM中)
- 更新中(重新渲染)
- 注销中(解除绑定)
React向三种状态都提供了钩子函数. will
方法在某些事件发生之前执行,did
方法在事件后执行.
var Hello = React.createClass({
getInitialState: function () {
return {
opacity: 1.0
};
},
componentDidMount: function () {
this.timer = setInterval(function () {
var opacity = this.state.opacity;
opacity -= .05;
if (opacity < 0.1) {
opacity = 1.0;
}
this.setState({
opacity: opacity
});
}.bind(this), 100);
},
render: function () {
return (
<div style={{opacity: this.state.opacity}}>
Hello {this.props.name}
</div>
);
}
});
ReactDOM.render(
<Hello name="world"/>,
document.getElementById('example')
);
接下来是 一整套生命周期的方法.
componentWillMount(): 只会执行一次, 在初始化渲染之前. 在这里关联事件监听是最好的.
this.setState
在这里不起作用.componentDidMount(): 只会执行一次, 在初始化渲染之后, 可以使用
this.getDOMNode()
—我试过这个方法(getDOMNode()
), 无论怎样都调不起来, 也不知道为什么.componentWillUpdate(object nextProps, object nextState): 在组件更新并已经绑定到DOM之后执行. 可以使用
this.getDOMNode()
进行更新操作.componentDidUpdate(object prevProps, object prevState): 在组件更新并刷新到DOM后立即调用. 这个方法不是继承自初始化的render方法. 当组件被更新后, 可以凭借这个方法操作DOM.
componentWillUnmount(): 当组件从DOM被解绑之前立即执行. 在这儿可以清楚事件监听函数甚至全部清除.
componentWillReceiveProps(object nextProps): 当组件正在新建新的props被执行. 依靠这些props, 你可能想用
this.setState
.shouldComponentUpdate(object nextProps, object nextState): 当收到新的props或者state之前被执行. 如果你知道一个更新没有必要的时候, 会
return false
.
Demo11: Ajax
从服务器或者接口上如何获取到数据? 解决方案就是: 在componentDidMount
函数中, 使用Ajax去抓取数据. 当收到服务器响应报文之后, 把数据用this.setState()
存储下来, 并根据相应状态去重新渲染UI.
var UserGist = React.createClass({
getInitialState: function() {
return {
username: '',
lastGistUrl: ''
};
},
componentDidMount: function() {
$.get(this.props.source, function(result) {
var lastGist = result[0];
if (this.isMounted()) {
this.setState({
username: lastGist.owner.login,
lastGistUrl: lastGist.html_url
});
}
}.bind(this));
},
render: function() {
return (
<div>
{this.state.username}'s last gist is
<a href={this.state.lastGistUrl}>here</a>.
</div>
);
}
});
ReactDOM.render(
<UserGist source="https://api.github.com/users/octocat/gists" />,
document.getElementById('example')
);
Demo12: Display value from a Promise
这个demo得益于Nat Pryce 的文章“Higher Order React Components”所写.
如果一个React组件的数据被异步接收了, 我们也可以把Promise对象当做组件的属性, 如下所示:
ReactDOM.render(
<RepoList
promise={$.getJSON('https://api.github.com/search/repositories?q=javascript&sort=stars')}
/>,
document.getElementById('example')
);
上面的代码是从 Github’s API获取数据, 并且RepoList
组件会获取到一个Promise对象作为自身属性.
现在, 当promise被阻塞时, 组件会展现一个loading标志. 当promise被成功解析, 组件会展现一个仓库信息的列表. 如果promise处于拒绝态, 组件会返回错误信息.
var RepoList = React.createClass({
getInitialState: function() {
return { loading: true, error: null, data: null};
},
componentDidMount() {
this.props.promise.then(
value => this.setState({loading: false, data: value}),
error => this.setState({loading: false, error: error}));
},
render: function() {
if (this.state.loading) {
return <span>Loading...</span>;
}
else if (this.state.error !== null) {
return <span>Error: {this.state.error.message}</span>;
}
else {
var repos = this.state.data.items;
var repoList = repos.map(function (repo) {
return (
<li>
<a href={repo.html_url}>{repo.name}</a> ({repo.stargazers_count} stars) <br/> {repo.description}
</li>
);
});
return (
<main>
<h1>Most Popular JavaScript Projects in Github</h1>
<ol>{repoList}</ol>
</main>
);
}
}
});
demo13 根本跑不起来, 除非找梯子FQ.