React 最佳實踐一、 React 與 AJAX
React 只負責處理 View 這一層,它本身不涉及網絡請求 /AJAX:
第一,用什么技術從服務端獲取數據;
第二,獲取到的數據應該放在 react 組件的什么位置。
事實上是有很多的:fetch()、fetch polyfill、axios...
其中最需要我們關注的是window.fetch(),它是一個簡潔、標準化的 javascript 的 Ajax API 。
在 Chrome 和 Firefox 中已經可以使用,如果需要兼容其他瀏覽器,可以使用 fetch polyfill 。
ajax實踐:
1.所有的數據請求和管理都存放在唯一的一個根組件讓父組件 /根組件集中發送所有的 ajax 請求,
把從服務端獲取的數據統一存放在這個組件的 state 中,再通過 props 把數據傳給子組件。
這種方法主要是針對組件樹不是很復雜的小型應用。缺點就是當組件樹的層級變多了以后,
需要把數據一層一層地傳給子組件,寫起來麻煩,性能也不好。2.設置多個容器組件專門處理數據請求和管理其實跟第一種方法類似,只不過設置多個容器組件來負責數據請求和狀態管理。
這里我們需要區分兩種不同類型的組件,
一種是展示性組件( presentational component ),
另一種是容器性組件( container component )。
展示性組件本身不擁有任何狀態,所有的數據都從容器組件中獲得,在容器組件中發送 ajax 請求。3.使用 Redux 或 Relay 的情況Redux 管理狀態和數據, Ajax 從服務器端獲取數據,所以很顯然當我們使用了 Redux 時,
應該把所有的網絡請求都交給 redux 來解決。具體來說,應該是放在Async Actions。
如果用其他類 Flux 庫的話,解決方式都差不多,都是在 actions 中發送網絡請求。
把計算和條件判斷都交給render()方法吧
echarts
概覽頁面 -》 echats圖修改的時候犯了個錯 yield put({ type: ' DescribeInvadeTrend' }); --》
發現沒有請求 咋回事!!!! 注意觀察代碼...
React setState
setState(updater, callback)這個方法是用來告訴react組件數據有更新,有可能需要重新渲染。它是異步的,react通常會集齊一批需要更新的組件,然后一次性更新來保證渲染的性能,所以這就給我們埋了一個坑:
那就是在使用setState改變狀態之后,立刻通過this.state去拿最新的狀態往往是拿不到的。
所以第一個使用要點就是:如果你需要基于最新的state做業務的話,可以在componentDidUpdate或者setState的回調函數里獲取。(注:官方推薦第一種做法)
設想有一個需求,需要在在onClick里累加兩次,如下:
onClick = () => {this.setState({ index: this.state.index + 1 });this.setState({ index: this.state.index + 1 });}
在react眼中,這個方法最終會變成
Object.assign(previousState,{index: state.index+ 1},{index: state.index+ 1},...
)
由于后面的數據會覆蓋前面的更改,所以最終只加了一次.所以如果是下一個state依賴前一個state的話,推薦給setState傳function
onClick = () => {this.setState((prevState, props) => {return {quantity: prevState.quantity + 1};});this.setState((prevState, props) => {return {quantity: prevState.quantity + 1};});
}
以上是使用setState的兩個注意事項,接下來我們來看看setState被調用之后,更新組件的過程:
ReactBaseClassses.js
ReactComponent.prototype.setState = function (partialState, callback) {// 將setState事務放進隊列中this.updater.enqueueSetState(this, partialState);if (callback) {this.updater.enqueueCallback(this, callback, 'setState');}
};
這里的partialState可以傳object,也可以傳function,它會產生新的state以一種Object.assgine()的方式跟舊的state進行合并。
二、enqueueSetState
enqueueSetState: function (publicInstance, partialState) {// 獲取當前組件的instancevar internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');// 將要更新的state放入一個數組里var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);queue.push(partialState);// 將要更新的component instance也放在一個隊列里enqueueUpdate(internalInstance);}
.
..消化...
state和props
React 的核心思想是組件化的思想,而React 組件的定義可以通過下面的公式描述:
UI = Component(props, state)
不是對props 和state 基本用法的介紹,而是嘗試從更深層次解釋props 和 state,并且歸納使用它們時的注意事項。
一句話概括,props 是組件對外的接口,state 是組件對內的接口。
組件的props 和 state都和組件最終渲染出的UI直接相關。
組件中用到的一個變量是不是應該作為組件state,可以通過下面的4條依據進行判斷:
- 這個變量是否是通過props從父組件中獲取?如果是,那么它不是一個狀態。?
- 這個變量是否在組件的整個生命周期中都保持不變?如果是,那么它不是一個狀態。?
- 這個變量是否可以通過state 或props 中的已有數據計算得到?如果是,那么它不是一個狀態。?
- 這個變量是否在組件的render方法中使用?如果不是,那么它不是一個狀態。這種情況下,這個變量更適合定義為組件的一個普通屬性(除了props 和 state以外的組件屬性 ),例如組件中用到的定時器,就應該直接定義為this.timer,而不是this.state.timer。?
不能依賴當前的props計算下個state,因為props的更新也是異步的。
Object.assign(previousState,{quantity: this.state.quantity + 1},{quantity: this.state.quantity + 1}
)
當點擊一次購買按鈕,購買的數量就會加1,如果我們連續點擊了兩次按鈕,就會連續調用兩次this.setState({quantity: this.state.quantity + 1})
// 正確
this.setState((preState, props) => ({counter: preState.quantity + 1;
}))
State 的更新是一個淺合并(Shallow Merge)的過程。
例如,一個組件的state為:
this.state = {title : 'React',content : 'React is an wonderful JS library!'
}
this.setState({title:?'Reactjs'});
React會合并新的title到原來的組件state中,同時保留原有的狀態content,合并后的state為:
{title : 'Reactjs',content : 'React is an wonderful JS library!'
}
State與Immutable
React官方建議把state當作不可變對象,一方面是如果直接修改this.state,組件并不會重新render;另一方面state中包含的所有狀態都應該是不可變對象。
如有一個數組類型的狀態books,當向books中增加一本書時,使用數組的concat方法或ES6的數組擴展語法(spread syntax):
// 方法一:使用preState、concat創建新數組
this.setState(preState => ({books: preState.books.concat(['React Guide']);
}))// 方法二:ES6 spread syntax
this.setState(preState => ({books: [...preState.books, 'React Guide'];
}))
當從books中截取部分元素作為新狀態時,使用數組的slice方法:
// 使用preState、slice創建新數組
this.setState(preState => ({books: preState.books.slice(1,3);
}))
當從books中過濾部分元素后,作為新狀態時,使用數組的filter方法:
this.setState(preState => ({books: preState.books.filter(item => {return item != 'React'; });
}))
注意不要使用push、pop、shift、unshift、splice等方法修改數組類型的狀態,因為這些方法都是在原數組的基礎上修改,而concat、slice、filter會返回一個新的數組。
狀態的類型是簡單對象(Plain Object)
this.state = {owner = {name: '老干部',age: 30}
}
this.setState(preState => ({owner: Object.assign({}, preState.owner, {name: 'Jason'});
}))
//第一種方法
this.setState(preState => ({owner: {...preState.owner, name: 'Jason'};
}))
//第二種方法
總結一下,創建新的狀態的關鍵是,避免使用會直接修改原對象的方法,而是使用可以返回一個新對象的方法。當然,也可以使用一些Immutable的JS庫,如Immutable.js,實現類似的效果。
那么,為什么React推薦組件的狀態是不可變對象呢?一方面是因為不可變對象方便管理和調試,了解更多可參考這里: http://redux.js.org/docs/faq/ImmutableData.html#benefits-of-immutability
另一方面是出于性能考慮,當組件狀態都是不可變對象時,我們在組件的shouldComponentUpdate方法中,僅需要比較狀態的引用就可以判斷狀態是否真的改變,從而避免不必要的render方法的調用。當我們使用React 提供的PureComponent時,更是要保證組件狀態是不可變對象,否則在組件的shouldComponentUpdate方法中,狀態比較就可能出現錯誤。
porps的改變為什么是異步的? ????????
React頂級API
React.Component
class Greeting extends React.Component {render() {return <h1>Hello, {this.props.name}</h1>}
}
ReactDOM.render(<Greeting name={"zhangyatao"}/>,document.getElementById('root)
)
React.PureComponet
它實現了shouldComponentUpdate()對props和state的進行淺比較。
React.PureComponent的shouldComponentUpdate()僅會對對象進行淺比較,如果對象包含復雜的數據結構,對于深層次的差異有可能會產生false-negatives(假陰性,相當于醫院中的錯診)。
React.Children
React.children提供了處理this.props.children中那些不透明的數據結構的一些工具函數。
React.Children.map
React.Children.map(children,?function[(thisArg))
React.Children.forEach(children,?function[(thisArg)])
和React.Children.map相同,只不過不會返回一個數組。
React.Children.count
React.Children.count(children)
返回children中的組件總數。
React.Children.toArray
React.Children.toArray(children)
將子元素中的不透明數據結構作為一個一維數組返回。如果你想在render方法中操作children集合,特別是如果你想在傳遞它之前重新排序或切割this.props.children,這個方法將非常有用。
React.PropTypes
React.PropTypes是一系列類型驗證器的集合,可以與組件的propTypes對象一起使用,以驗證傳遞到組件的props。
深入理解JSX
從根本上講,JSX就是提供了一個React.createElement(component, props, ...children)函數的語法糖。就像下面的JSX代碼:
<MyButton color="blue" shadow={2}>Click Me
</MyButton>
經過編譯后為:
React.createElement(MyButton,{color: 'blue', shadow: 2},'Click Me'
)
<div className="sidebar"?/>
經過編譯后為:
React.createElement('div',{className: 'sidebar'},null
)
如果你想測試一些特定的JSX是如何轉換成JavaScript的話,你可以試試在線Babel編譯器。
由于JSX編譯的本質是對React.createElement的調用,因此React庫也必須始終在JSX代碼的作用域中。
還可以使用JSX中的點表示符來引用React組件
const MyComponents = {DatePicker(props) {return <div>這里有一個顏色為{props.color}的日期選擇器</div>}
};function BlueDataPicker(props) {return <MyComponents.DatePicker color="blue" />
}
字符串直接量
<MyComponent message="hi " /><MyComponent message={'hi'} />
<MyComponent message='<3' /><MyComponent message={'<3'} />
Props傳遞
如果你有一個對象類似的數據作為props,并且想在JSX中傳遞它,你可以使用...作為一個“spread”運算符傳遞整個props對象。 這兩個組件是等效的:
function App() {return <Greeting firstName="yatao" lastName="zhang" />;
}function App() {const props = {firstName: 'yatao', lastName: 'zhang'};return <Greeting {...props} />;
}
JSX中的子元素和子組件
function MyComponent(props) {return <div>{props.children}<div>; //=> <div>hello</div>
}
JSX會刪除行的開始和結尾處的空格。 它也會刪除中間的空行。 與標簽相鄰的空行被會被刪除;
在字符串文本中間出現的空行會縮合成一個空格。 所以這些都渲染相同的事情:布爾值、null、undefined在渲染時會被自動忽略
<div></div><div>{false}</div><div>{null}</div><div>{true}</div>
如果你想要一個值如false,true,null或undefined出現在輸出中,你必須先將它轉換為字符串:
react性能查看工具
在最新的React16版本中,我們可以直接在url后加上?react_pref,就可以在chrome瀏覽器的performance,我們可以查看User Timeing來查看組件的加載時間。

ps: 關于key的使用
關于key的使用我們要注意的是,這個key值要穩定不變的,就如同身份證號之于我們是穩定不變的一樣。
一個常見的錯誤就是,拿數組的的下標值去當做key,這個是很危險的,代碼如下,我們一定要避免。
將key設置為一個特殊字段,保證其唯一性
其他的檢測工具
react-perf-tool?為React應用提供了一種可視化的性能檢測方案,該工程同樣是基于React.addons,但是使用圖表來顯示結果,更加方便。