## 聲明式
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);
~~~