## 声明式
React 可以无痛的创建交互UIs。在你的应用中每个状态设计简单的视图,当数据改变的时候,React 可以高效的更新和渲染正确的组件。
声明式视图使你的代码更加可预测和容易调试。
## 基于组件
构建管理它们自己状态的组件的封装,然后用它们组成复杂的UIs 。
由于组件逻辑是使用 JavaScript 编写而不是模板,你可以通过 app 传递丰富的数据并在 DOM 外保持状态。
## 一处学习,处处编写
我们没有假定你其余的技术栈,所以你可以在 React 中开发新的功能而不用重写已有代码。
React 也可以使用 node 在服务器端渲染,也可以通过 [React native](https://facebook.github.io/react-native/) 提供对移动 app 的支持。
## 一个简单的组件实例
React 组件实现了一个 render() 方法,获取输入数据并返回要显示的内容。这个例子使用一个 XML 类似的语法,称为 JSX。被传递到组件的输入数据,可以通过 this.props 被 render() 访问。
JSX 是可选的,并不是使用 React 必须的选项。可以看看编译后的 JS 来查看 JSX 编译器生成的源 JavaScript 代码。
JSX代码:
~~~
class HelloMessage extends React.Component {
render() {
return <div> Hello {this.props.name}</div>
}
}
ReactDom.render(<HelloMessage name="John" />, mountNode)
~~~
编译后的JS:
~~~
class HelloMessage extends React.Component {
render() {
return React.createElement(
"div",
null,
"Hello ",
this.props.name
);
}
}
ReactDOM.render(React.createElement(HelloMessage, { name: "John" }), mountNode);
~~~
## 一个有状态的组件实例
除了获取输入数据(通过 this.props 访问),组件还可以维护内部的状态数据(通过 this.state 访问)。当一个组件的状态数据发生变化,渲染的标记会通过重新调用 render() 被更新。
JSX代码:
~~~
class Timer extends React.Component {
constructor(props) {
super(props)
this.state = {secondsElapsed : 0}
}
tick() {
this.setState( (prevState)=>({
secondsElapsed: prevState.secondsElapsed + 1
}) )
}
componentDidMount(){
this.interval = setInterval(()=>this.tick(), 1000)
}
componentWillMount(){
clearInterval(this.interval)
}
render(){
return (
<div>Seconds elapsed: {this.state.secondsElapsed}</div>
)
}
}
ReactDOM.render(<Timer />, mountNode)
~~~
编译后的JS:
~~~
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = { secondsElapsed: 0 };
}
tick() {
this.setState(prevState => ({
secondsElapsed: prevState.secondsElapsed + 1
}));
}
componentDidMount() {
this.interval = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return React.createElement(
"div",
null,
"Seconds Elapsed: ",
this.state.secondsElapsed
);
}
}
ReactDOM.render(React.createElement(Timer, null), mountNode);
~~~
## 一个应用实例
使用 props 和 state ,我们可以组合成一个小型的 Todo 应用。这个例子使用 state 跟踪当前项的列表和用户已经键入的文本。虽然事件处理程序出现被内联渲染,它们会被事件委托修正和实现。
JSX代码:
~~~
class TodoApp extends React.Component {
constructor(props){
super(props)
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.state = { items:[], text:''}
}
render(){
return(
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={this.state.text} />
<button>{'Add #' + (this.state.items.length + 1)}</button>
</form>
</div>
)
}
handleChange(e){
this.setState({text:e.target.value})
}
handleSubmit(e){
e.preventDefault()
let newItem = {
text:this.state.text,
id:Date.now()
}
this.setState((prevState)=>({
items:prevState.items.concat(newItem),
text:''
}))
}
}
class TodoList extends React.Component {
render(){
return (
<ul>
{this.props.items.map(item=>(
<li key={item.id}>{item.text}
</li>
))}
</ul>
)
}
}
ReactDOM.render(<TodoApp />, document.getElementById('mount-node'))
~~~
JS代码:
~~~
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = { items: [], text: '' };
}
render() {
return React.createElement(
'div',
null,
React.createElement(
'h3',
null,
'TODO'
),
React.createElement(TodoList, { items: this.state.items }),
React.createElement(
'form',
{ onSubmit: this.handleSubmit },
React.createElement('input', { onChange: this.handleChange, value: this.state.text }),
React.createElement(
'button',
null,
'Add #' + (this.state.items.length + 1)
)
)
);
}
handleChange(e) {
this.setState({ text: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
var newItem = {
text: this.state.text,
id: Date.now()
};
this.setState(prevState => ({
items: prevState.items.concat(newItem),
text: ''
}));
}
}
class TodoList extends React.Component {
render() {
return React.createElement(
'ul',
null,
this.props.items.map(item => React.createElement(
'li',
{ key: item.id },
item.text
))
);
}
}
ReactDOM.render(React.createElement(TodoApp, null), mountNode);
~~~
## 一个使用外部插件的组件
React 非常灵活,提供了你跟其它库和框架交互的钩子。这个例子使用了 remarkable,一个外部的 Markdown 库,来实时转换 textarea 的值。
JSX代码:
~~~
class MarkdownEditor extends React.Component {
constructor(props){
super(props)
this.handleChange = this.handleChange.bind(this)
this.state = {value:'Type some *markdown* here !'}
}
handleChange(){
this.setState({value:this.refs.textarea.value})
}
getRawMarkup(){
let md = new Remarkable()
return {__html:md.render(this.state.value)}
}
render(){
return (
<div className="MarkdownEditor">
<h3>Input</h3>
<textarea onChange={this.handleChange} ref="textarea" defaultValue={this.state.value}></textarea>
<h3>Output</h3>
<div className="content" dangerouslySetInnerHTML={this.getRawMarkup()}></div>
</div>
)
}
}
ReactDOM.render(<MarkdownEditor />, document.getElementById('mount-node'))
~~~
JS 代码:
~~~
class MarkdownEditor extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = { value: 'Type some *markdown* here!' };
}
handleChange() {
this.setState({ value: this.refs.textarea.value });
}
getRawMarkup() {
var md = new Remarkable();
return { __html: md.render(this.state.value) };
}
render() {
return React.createElement(
"div",
{ className: "MarkdownEditor" },
React.createElement(
"h3",
null,
"Input"
),
React.createElement("textarea", {
onChange: this.handleChange,
ref: "textarea",
defaultValue: this.state.value }),
React.createElement(
"h3",
null,
"Output"
),
React.createElement("div", {
className: "content",
dangerouslySetInnerHTML: this.getRawMarkup()
})
);
}
}
ReactDOM.render(React.createElement(MarkdownEditor, null), mountNode);
~~~