著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
PS:轉載請註明出處
作者:TigerChain
地址:http://www.jianshu.com/p/44a787904d9b
本文出自TigerChain簡書
React 教程系列
教程簡介
1、閱讀對象
本篇教程適合新手閱讀,老手直接略過
2、教程難度
初級
注:本篇文章採用ES6的寫法,以後不沒有特別說明都使用ES6+yarn+webpack去寫demo 1、什麼是state
我們都說React是一個狀態機器,體現是什麼地方呢,就是體現在state上,通過與使用者的互動,實現不同的狀態,然後去渲染UI,這樣就讓使用者的資料和介面保持一致了。state是組件的私人屬性。
在React中,更新群組件的state,結果就會重新渲染使用者介面(不需要操作DOM),一句話就是說,使用者的介面會隨著狀態的改變而改變。
PS: state 只能在本組件中去初始化,並且 state 只能被本組件去修改和仿問,不能被外部仿問和修改,所以也可以說,state 是組件私人的。 2、state的使用方法
1、初始化state
對於state的使用方法,在ES5中和Es6中使用是不一樣的,我們以ES6為例,在組件的構方法中如下設定即可,設定一個預設的state屬性。
constructor(props) { super(props); this.state={ key:value, ... }}
2、更新state
更新state調用以下方法
this.setState({ key:value }) ;
千萬不要這麼幹,this.state.key = XXX ; 這樣不會重新渲染介面
注意:setState()是非同步,也就是你調用了setState()之後,React就開始準備去更新了,中間計算會可能會有一定的延時。
PS:如果調用了setState()方法以後,就會調用render()方法,也就是前面說的,使用者的介面會隨著狀態的改變而改變。
3、調用diff演算法
這一步是在2步的基礎上的,setState()會觸發diff演算法最終確定是否要更新。 3、什麼樣的組件應該有state
應該說大部分的組件根據props來取得資料並渲染,但是使用者輸入,請求伺服器,操作資料庫等情況下就需要state了.官方的建議是盡量構建無狀態的組件,是為了提高效能,減少渲染次數,做法就是構建幾個無狀態的組件,在這之上構建一個有狀態和使用者和服務互動 ,然後通過props來傳遞給無狀態的組件。
使用state要注意三個事情 1、不要直接的修改state
反例 請不要這樣做this.state.comment = 'Hello';
正確的做法是使用setState()方法
正確的做法this.setState({ comment:'Hello',})
2、狀態更新是非同步
用官方的話說就是this.props和this.state更新的時候可能是非同步,所以你不應該依靠他們的值來計算下一個狀態
以下是錯誤的例子
反例this.setState({ counter: this.state.counter + this.props.increment,});
正確的例子
正確的例子this.setState((prevState, props) => ({ counter: prevState.counter + props.increment}))
3、state的更新是合并後的
當你調用 setState() ,React會合并你提供的對象到當前的 state 裡
比如:你的state可能包含幾個獨立的變數
constructor(props) { super(props); this.state = { posts: [], comments: [] }; }
然後你可以通過調用setState()來獨立的更新他們
componentDidMount() { fetchPosts().then(response => { this.setState({ posts: response.posts }); }); fetchComments().then(response => { this.setState({ comments: response.comments }); }); }
這個合并是淺合并,所以 this.setState({comments}) 會讓 this.state.posts 完整,但是會完全替換掉 this.state.comments . 4、開始擼碼
說一千,道一萬,都是在理論層級,下面我們從0開始寫一個簡單的Demo來體驗一下state。下面使用webpack+yarn來寫一個demo。
在mac環境下,win下基本差不太多
1、在指定目錄下建立state目錄(命令列中操作)
mkdir state
2、使用yarn初始化項目(命令列中操作)
cd stateyarn init
在上面步驟一路斷行符號即可
3、安裝一些依賴(命令列中操作)
yarn add react react-dom webpack webpack-dev-server babel-core babel-loader babel-preset-react bable-preset-es2015 babel-preset-stage-0 --dev
然後斷行符號,等待安裝依賴,如果沒有什麼問題就可以在package.json中看到如下結果(紅色框中)
以上就表明你的依賴安裝成功了
4、在state根目錄中建立.babelrc並輸入以下內容
{ "presets":["react","es2015","stage-0"]}
5、在state目錄中分別建立app和public檔案夾
6、在public中建立index.html並輸入以下內容
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Hello state</title></head><body> <div id="container"></div> <script src="bundle.js"></script></body></html>
7、在app目錄中分別建立main.js和State.js
# main.jsimport React from 'react' ;import ReactDOM from 'react-dom' ;import State from './State.js' ;ReactDOM.render( <State />, document.getElementById('container')) ;
# State.jsimport React, { Component, PropTypes } from 'react';/** * 使用es6文法 定義一個State組件 */export default class State extends Component { constructor(props) { super(props); this.state = { //初始化state countnum:0, }; } /** * 點擊事件方法 countnum+1 */ _handlerEvent(){ this.setState({ countnum:this.state.countnum+1, }) } render() { return (<div> {this._renderView()} </div>); } /** * 渲染一個button組件 */ _renderView(){ return( <div> <button onClick={this._handlerEvent.bind(this)}>點擊{this.state.countnum}次</button> </div> ); }}
8、在state根目錄中建立webpack.config.js並且輸入以下內容,具體不清楚的可以看webpack這一節:http://www.jianshu.com/p/732c4d501668
module.exports = { entry:__dirname+'/app/main.js', output:{ path:__dirname+'/public', filename:'bundle.js' }, devServer:{ contentBase: "./public",//本機伺服器所載入的頁面所在的目錄 historyApiFallback: true,//不跳轉 inline: true//即時重新整理 }, module:{ loaders:[ //babel配置 { test:/\.js$/, exclude: /node_modules/, loader: 'babel-loader' } ] }}
9、在package.json中添加指令碼(這不是必須的,但是可以方便調用)
圖中的藍色部分就是我們添加的指令碼,即:
"scripts":{ "start":"webpack-dev-server --progress --port 8888" }
10、在命令列直接運行 yarn start 如果沒有什麼問題,我們在瀏覽器上輸入localhost:8888,會看到下面的結果
以上我們就完成了一個簡單的state的例子,從這個例子中我們可以看到每當我點擊按鈕的時候組件的狀態都會發生改變,計數就加1了,但是當我們調用了setState()方法到底有沒有重新整理介面呢。我們再測試一下
11、接著上面的例子我們繼續修改State.js
從圖中可知我們只是在原有的基礎上添加了現代戰爭方法,用來測試render方法調用次數,然後儲存,看結果(開啟chrome的調試欄)
從圖中我們可以清楚的看到,我們點擊了多少次,render方法就調用了多少次,所以說當調用了setState()方法就會重新渲染介面
到此為止,我們對state就有一個大體的認知了。下面我們來說一個無狀態組件的例子。 5、無狀態的組件 1、什麼是無狀態組件
無狀態組件顧名思義就是沒有狀態的組件我們上面提到過無狀態的組件,官方建議的是盡量讓組件無狀態化,但是肯定有一個地方要狀態化的,一般做法是把要組合的組件無狀態化,宿主組件有一個狀態設定(一般情況下,也就是父組件更新子組件的時候,但是有時會反過來)
接下來我們構建一個無狀態的組件,然後通過和使用者互動(點擊)的時候把狀態通過props傳給無狀態的組件達到更新UI的目的,這麼說可能有點繞我們看下面圖來直觀的理解一下
上圖就是這一過程的一個虛擬碼,很好理解。 2、我們通過一個demo來直觀感受一下
1、接著上面的例子,我們繼續在app中分別建立PassStateOfprops.js和StateLess.js
# PassStateOfprops.js 此組件可以當作有狀態的父組件import React, { Component, PropTypes } from 'react';import StateLess from './StateLess.js' ;/** * 通過 props來傳遞state 達到更新群組件的目的 */export default class PassStateOfprops extends Component { constructor(props) { super(props); this.state = { //初始化state list:[ '111', '222', '333' ] } } /** * 渲染介面 */ render() { return (<div> {this._renderButton()} {/* 將state通過props傳遞到無狀態組件StateLess中 */} <StateLess datas={this.state.list}/> </div>); }/** * 渲染一個button */ _renderButton(){ return( <div> <button onClick={this._handlerEvent.bind(this)}>改變值</button> </div> ) ; } /** * 點擊事件 */ _handlerEvent(){ this.setState({ list:['444','555','666'] }) ; }}
# StateLess.js 定義一個無狀態的組件import React, { Component, PropTypes } from 'react';/** * 定義一個無狀態的組件 */export default class StateLess extends Component { render() { return (<div> <ul> { this.props.datas.map(function (data) { return ( <li>{data}</li> ) }) } </ul> </div>); }}
2、修改main.js中代碼
其中黃色部分就是我們修改的部分.
3、查看結果
我們看到通過props傳遞state到無狀態的組件成功運行了,所以我們以後在項目中開發,盡量使用無狀態化的組件,把這種思想和習慣從現在就養成。 本demo的地址
https://github.com/githubchen001/react-lesson/tree/master/lesson02/09-state