代碼以下:javascript
import React, { Component } from 'react'; class Hello extends Component { // ... }
代碼以下:html
import React, { Component } from 'react'; import { render } from 'react-dom'; // Parent Component class GroceryList extends Component { render() { return ( <ul> <ListItem quantity = "1" > Bread </ListItem> <ListItem quantity = "6" > Eggs </ListItem> <ListItem quantity = "2" > Milk </ListItem> </ul> ); } } // Child Component class ListItem extends Component { render() { return (<li> {this.props.quantity}× {this.props.children} </li>); } } render(<GroceryList /> , document.getElementById('root'));
從上而下或者從下而上,儘可能讓app.js保持簡單,只包含數據(數據來自於API)並只渲染出一個KanbanBoard組件java
import React from 'react'; import { render } from 'react-dom'; import KanbanBoard from './KanbanBoard'; let cardsList = [ { id: 1, title: "Read the Book", description: "I should read the whole book", status: "in-progress", tasks: [] }, { id: 2, title: "Write some code", description: "Code along with the samples in the book", status: "todo", tasks: [ { id: 1, name: "ContactList Example", done: true }, { id: 2, name: "Kanban Example", done: false }, { id: 3, name: "My own experiments", done: false } ] } ]; render( <KanbanBoard cards = { cardsList } />, document.getElementById('root'));
代碼以下:node
return ( <h1>Hello World</h1> ) // 合法 return ( <h1>Hello World</h1> <h2>Have a nice day</h2> ) // 不合法
解決方案1、使用三元表達式react
render(){ return ( <div className={condition ? "show":"hide"}> Hello JSX </div> ) } // 或者 <div> {condition? <span> Hello JSX </span> : null} </div>
解決方案2、將條件外置npm
render(){ let className; if(condition) { className = "show"; } else { className = "hide"; } } return ( <div className={className}>Hello JSX</div> )
方式、1json
var HelloMessge = React.createClass({ render: <div dangerouslySetInnerHTML={{ __html: '<h3>hahhah</h3>' }}> </div> })
方式、2redux
destroy() { if (this._el) { this._el.querySelector('.dialog__mask').classList.add('maskFadeOut') this._el.querySelector('.dialog__wrapper').classList.add('wrapperFadeOutUp') setTimeout(()=>{ ReactDOM.unmountComponentAtNode(this._el) document.body.removeChild(this._el) this._el = null }, 150) } } open() { this._el = document.createElement('div') document.body.appendChild(this._el) ReactDOM.unstable_renderSubtreeIntoContainer( this, this.renderDialog(), this._el ); // 更新組件到傳入的 DOM 節點上,完成在組件內實現跨組件的 DOM 操做 } renderDialog() { const { skin, width, okBtn, okBtnText, children, cancelBtn, cancelBtnText } = this.props; return ( <div className="dialog" key="dialog"> <div className="dialog__mask maskFadeIn dialog_animated" style={{height: (document.body.offsetHeight > window.screen.height ? document.body.offsetHeight : window.screen.height) + 'px'}} /> <div className={'dialog__wrapper wrapperFadeInDown dialog_animated dialog__wrapper--skin-' + skin} style={{left:'50%', top: (window.screen.height/2 - 100) + 'px', width: width + 'px', marginLeft: (width*(-1)/2) + 'px'}} > <div className="dialog__content"> {children} </div> {(okBtn || cancelBtn) && ( <div className="dialog__btns"> {okBtn && (<button className="dialog__btn dialog__btn--ok" onClick={this.onOk}>{okBtnText}</button>)} {cancelBtn && <button className="dialog__btn dialog__btn--cancel" onClick={this.onCancel}>{cancelBtnText}</button>} </div> )} </div> </div> ) }
一:生成包含新功能的新組件數組
function hoc(Comp){ return class NewComponent extends Component { // 增長或者修改的功能實現 extendFunc(){ } render() { return ( <Comp {... this.props} /> ) } } } const NewCompent = hoc(Comp);
二:控制props瀏覽器
const MyContainer = (WrappedComponent) => class extends Component { render() { const newProps = { text: newText }; return <WrappedComponent {...this.props} {...newProps} />; } }
屬性轉換
function transProps(transFn){ return function(Comp){ return class extends Component { render(){ return <Comp {...transFn(this.props)} /> } } } }
三:抽象state,高階組件能夠將原組件抽象爲展現型組件,分離內部狀態
const MyContainer = (WrappedComponent) => class extends Component { constructor(props){ super(props); this.state = { name: '', }; this.onNameChange = this.onNameChange.bind(this); } onNameChange( event ) { this.setState( name: event.target.value, ) } render() { const newPros = { name: { value: this.state.name, onChange: this.onNameChange, } } return <WrappedComponent {...this.props} {...newProps} />; } }
四:封裝異步請求
function hocListWithData({dataLoader, getListFromResultData}) { return Com => { return class extends Component { constructor(props){ super(); this.state = { resultData: undefined } } componentDidMount(){ dataLoader() .then(data=> this.setState({resultData: data}) } render(){ return { <Comp {...getListFromResultData(this.state.resultData)} {...this.props} /> } } } } }
舉個例子,在javascript中
var a = {a:1}; var b = a; b.a = 2; // => a.a = 2
由上面的例子能夠知道,可變數據有時使用起來是會有問題的,在各類數據操做後有可能會使原數據被污染而致使程序出錯,纔出現immutable.js不可修改數據類型的概念,所以,修改組件狀態時,永遠不能直接寫:
this.state.arr = newArr; // 或者 const tempArr = this.state.arr; temp.push('hello'); // 此時已經修改了this.state了,不能這樣寫 this.state.setState({newArr,tempArr })
可使用很是侵入式的純函數如:map、filter、concat或者Object.assign({}, this.state.xxx, {newKey, newValue})來解決這個問題
immutable例子以下:
import React from 'react' import { Map, Set } from 'immutable'; export default class Clock extends React.Component { constructor(props) { super(props); this.state = { immutableData: Map({ date: new Date(), name: 'Nelson won\'t change, but time is changing all the time!' }) }; this.dataSet = Set(); } componentDidMount() { this.timerID = setInterval( () => this.tick(), 10 ); let dataSet = Set(); dataSet = dataSet.add(1); dataSet = dataSet.add(2); this.dataSet = dataSet; } componentWillUnmount() { clearInterval(this.timerID); } tick() { this.setImmutableState('date',new Date()); } setImmutableState(key, value) { this.setState({ immutableData: this.state.immutableData.set(key, value) }); } render() { const formatTime = date => { let hour = date.getHours(); let minute = date.getMinutes(); let second = date.getSeconds(); let milisecond = Math.floor(date.getMilliseconds() / 10); if (hour < 10) { hour = '0' + hour; } if (minute < 10) { minute = '0' + minute; } if (second < 10) { second = '0' + second; } if (milisecond < 10) { milisecond = '0' + milisecond; } return `${hour} : ${minute} : ${second} : ${milisecond}`; } return ( <div> <h2>如今時間 {formatTime(this.state.immutableData.get('date'))}. Hello {this.state.immutableData.get('name')}. dataSetSize:{this.dataSet.size}</h2> </div> ); } }
npm install --save whatwg-fetch
import 'whatwg-fetch';
npm install --save babel-polyfill
以下面action代碼的寫法,組件中能夠直接寫 dispatch(fetchPostsIfNeeded) 分發異步 action:
export const requestPosts = reddit => ({ type: REQUEST_POSTS, reddit }) export const receivePosts = (reddit, json) => ({ type: RECEIVE_POSTS, reddit, posts: json.data.children.map(child => child.data), receivedAt: Date.now() }) const fetchPosts = reddit => dispatch => { dispatch(requestPosts(reddit)) return fetch(`https://www.reddit.com/r/${reddit}.json`) .then(response => response.json()) .then(json => dispatch(receivePosts(reddit, json))) } const shouldFetchPosts = (state, reddit) => { const posts = state.postsByReddit[reddit] if (!posts) { return true } if (posts.isFetching) { return false } return posts.didInvalidate } export const fetchPostsIfNeeded = reddit => (dispatch, getState) => { if (shouldFetchPosts(getState(), reddit)) { return dispatch(fetchPosts(reddit)) } }
自定義的middleware通常能夠用作處理複雜的異步流,除了redux-thunk, redux-saga這些很是經常使用的中間件,咱們能夠本身定義一些中間件:
如處理輪詢、多異步串聯、修改並從新封裝action的值等,
通常的寫法以下:
// 如多異步串聯 const sequenceMiddleware = {dispatch, getState} => next => action => { if(!Array.isArray(action)){ return next(action); } return action.reduce((result, currAction) => { return result.then() => { return Array.isArray(currAction) ? Promise.all(currAction.map(item => dispatch(item))) : dispatch(currAction); }) }, Promise.resolve()); } // 或者這樣寫 export default store => next => action => { // ... }
代碼引用自:《深刻react技術棧》
{ type: 'ADD_TODO', payload: { text: 'Do something.' } } // 一個action必須是一個普通的JavaScript對象,有一個type字段 // 一個action可能有error字段、payload字段、meta字段。 // 一個action必須不能包含除type、payload、error及meta之外的其餘字段。
yield put({ type: 'INCREMENT' }) // put: 分發一個type爲'INCREMENT'的action 到 Store yield call(delay, 1000) // 不直接執行delay函數,用call便於跟蹤及測試 yield call([obj, obj.method], arg1, arg2, ...) // 如同 obj.method(arg1, arg2 ...) yield apply(obj, obj.method, [arg1, arg2, ...]) // call 和 apply 很是適合返回 Promise 結果的函數 const content = yield cps(readFile, '/path/to/file') // cps 能夠用來處理 Node 風格的函數 (例如,fn(...args, callback) 中的 callback 是 (error, result) => () 這樣的形式,cps 表示的是延續傳遞風格(Continuation Passing Style)) yield* takeEvery('FETCH_REQUESTED', fetchData) // takeEvery 監聽並容許多個 fetchData 實例同時啓動 yield* takeLatest('FETCH_REQUESTED', fetchData) // takeLatest 監聽並只容許執行一個 fetchData 任務。而且這個任務是最後被啓動的那個。 若是以前已經有一個任務在執行,那以前的這個任務會自動被取消 // 錯誤處理 function* fetchProducts() { try { const products = yield call(Api.fetch, '/products') yield put({ type: 'PRODUCTS_RECEIVED', products }) } catch(error) { yield put({ type: 'PRODUCTS_REQUEST_FAILED', error }) } } // 簡單的logger export function* watchAndLog() { while (true) { const action = yield take('*') // 監聽全部action const state = yield select() // select做用和 redux thunk 中的 getState 相同 console.log('action', action) console.log('state after', state) } } // select內容 const id = yield select(state => state.id); const tokenTask= yield fork(authorize, user, password) // 相比起call來講,fork是無阻塞調用 yield cancel(tokenTask) // 可取消task // 正確寫法, effects 將會同步並行執行 const [users, repos] = yield [ call(fetch, '/users'), call(fetch, '/repos') ] // 當咱們須要 yield 一個包含 effects 的數組, generator 會被阻塞直到全部的 effects 都執行完畢,或者當一個 effect 被拒絕 (就像 Promise.all 的行爲)。 const {posts, timeout} = yield race({ posts : call(fetchApi, '/posts'), timeout : call(delay, 1000) }) // race Effect 提供了一個方法,在多個 Effects 之間觸發一個競賽(race)。它會自動取消那些失敗的 Effects // yield* 對 Sagas 進行排序,可使用內置的 yield* 操做符來組合多個 Sagas,使得它們保持順序,以下: function* playLevelOne(getState) { /*...*/ } function* playLevelTwo(getState) { /*...*/ } function* playLevelThree(getState) { /*...*/ } function* game(getState) { const score1 = yield* playLevelOne(getState) put(showScore(score1)) const score2 = yield* playLevelTwo(getState) put(showScore(score2)) const score3 = yield* playLevelThree(getState) put(showScore(score3)) } // 任務的取消 // 或直接使用 `isCancelError(error)` if(error instanceof SagaCancellationException) yield put(actions.requestFailure('Sync cancelled!'))
_.union([1, 2], [4, 2], [2, 1]); // 字符或者數字數組去重 // => [1, 2, 4] var users = { 'data': [{ 'user': 'barney' }, { 'user': 'fred' }] }; var ages = { 'data': [{ 'age': 36 }, { 'age': 40 }] }; _.merge(users, ages); // 合併對象數組 // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] } // using a customizer callback var object = { 'fruits': ['apple'], 'vegetables': ['beet'] }; var other = { 'fruits': ['banana'], 'vegetables': ['carrot'] }; _.merge(object, other, function(a, b) { // 按函數合併對象數組 if (_.isArray(a)) { return a.concat(b); } }); // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] } _.zip(['fred', 'barney'], [30, 40], [true, false]); // 數組按順序組合成新數組 // => [['fred', 30, true], ['barney', 40, false]]
humps.camelize('hello_world') // 'helloWorld' humps.decamelize('fooBar') // 'foo_bar' humps.decamelize('fooBarBaz', { separator: '-' }) // 'foo-bar-baz' var object = { attr_one: 'foo', attr_two: 'bar' } humps.camelizeKeys(object); // { attrOne: 'foo', attrTwo: 'bar' } var array = [{ attr_one: 'foo' }, { attr_one: 'bar' }] humps.camelizeKeys(array); // [{ attrOne: 'foo' }, { attrOne: 'bar' }]
// 源數據 { "id": "123", "author": { "id": "1", "name": "Paul" }, "title": "My awesome blog post", "comments": [ { "id": "324", "commenter": { "id": "2", "name": "Nicole" } } ] } // 處理 import { normalize, schema } from 'normalizr'; // Define a users schema const user = new schema.Entity('users'); // Define your comments schema const comment = new schema.Entity('comments', { commenter: user }); // Define your article const article = new schema.Entity('articles', { author: user, comments: [ comment ] }); const normalizedData = normalize(originalData, article); // => { result: "123", entities: { "articles": { "123": { id: "123", author: "1", title: "My awesome blog post", comments: [ "324" ] } }, "users": { "1": { "id": "1", "name": "Paul" }, "2": { "id": "2", "name": "Nicole" } }, "comments": { "324": { id: "324", "commenter": "2" } } } }
const cart = json.carts.find(cart => cart.cartId === id); Object.assign({}, obj, obj); // reduce // map // filter ...還有 ... spread函數
onDelete={(e) => {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
onDelete(todo.id)
}
}
參考:Code Splitting in Create React App
參考:React Component Lifecycle(生命週期)和 React組件生命週期小結
一、setState是異步的。
二、setState會形成沒必要要的渲染,新的 state 其實和以前的有多是同樣的。這個問題一般能夠經過 shouldComponentUpdate 來解決。也能夠用 pure render 或者其餘的庫賴解決這個問題。
三、setState並不能頗有效的管理全部的組件狀態
引用自:http://blog.csdn.net/u012125579/article/details/69400169
mobx 最最核心的概念只有2個。 @observable 和 @observer ,它們分別對應的是被觀察者和觀察者。這是你們常見的觀察者模式,不過這裏使用了,ES7 中的 裝飾器。
核心概念2 actions 和 computed values,在 Component 中調用,這樣經過 action 的方法,就避免了直接修改 props 的問題。能夠經過引入 mobx 定義的嚴格模式,強制使用 action 來修改狀態。mobx 提供了 computed 裝飾器,用於獲取由基礎 state 衍生出來的值
import React, {Component} from 'react'; import { render } from 'react-dom'; import {observable, action, computed,useStrict} from 'mobx'; import {observer} from 'mobx-react'; useStrict(true); class Store { @observable todos = [{ // 被觀察者 title: "todo標題", done: false, },{ title: "已經完成 todo 的標題", done: true, }]; @action changeTodoTitle({index,title}){ this.todos[index].title = title } @computed get unfinishedTodos () { return this.todos.filter((todo) => todo.done) } } @observer // 觀察者 class TodoBox extends Component { render() { console.log('render'); return ( <div> <ul> { /* 把 unfinishedTodos 換成 todos,點擊修改標題就會在控制檯打印 "render".*/ } {this.props.store.unfinishedTodos.map( (todo,index) => <li key={index}>{todo.title}</li> )} </ul> <div> <input type="button" onClick={() => { this.props.store.changeTodoTitle({index:0,title:"修改後的todo標題"}); }} value="修改標題"/> </div> </div> ) } } const store = new Store(); render( <TodoBox store={store} />, document.getElementById('root') );
緣由是 package.json 依賴包中既有 babel 7.0 版本,又有 babel 6.0 版本,就會報這個錯誤
解決方案:要麼所有bebel相關的包升級到7.0,要麼所有降級到6.0版的,再不行檢查一下所有安裝的babel版本,刪除node_modules從新裝包