Awesome Redux 包含文檔,redux項目,以及一些腳手架工具html
reactjs101node
full stack redux tutorialreact
慕課網git
the-soundcloud-client-in-react-reduxgithub
參考資料:npm
觀看example的時候,要把整個脈絡action--》reducer---》store---》搞清楚,好比reducer要書寫,參考(action--》reducer這條線)因此須要action這個參數入侵。好比store要書寫,須要reducer--》store,因此須要reducer入侵。redux
代碼的編寫參考例子。api
有時候cnpm不起做用,多是被360攔截了。數組
添加信任就能夠了。
一個入門demo,描述了redux如何運做的過程,從action--》reducer---》store---》更新應用state的過程。
注意點:redux採用的是消費訂閱者模式。
commonjs方式的demo。簡易版的demo,方便了解整個rudux過程。
Redux 結合 React 使用的最基本示例。出於簡化,當 store 發生變化,React 組件會手動從新渲染。在實際的項目中,可使用 React 和 Redux 已綁定、且更高效的 React Redux。
要點
onIncrement={() => store.dispatch({type:'INCREMENT'})}
關聯store,讓store去dispatch相應的動做,根據subscribe到store的reducer去判斷類型,進行操做。這也是redux的底層的操做。import React,{Component} from 'react' /*class Counter extends Component { }*/ const incrementIfOdd = (value,onIncrement) => { if (value % 2 !== 0) { onIncrement() } } const incrementAsync = (onIncrement) => { setTimeout(onIncrement, 1000) } export default ({value,onIncrement,onDecrement})=>{ return ( <p> Clicked: {value} times <button onClick={onIncrement}> + </button> <button onClick={onDecrement}> - </button> <button onClick={e=>{ incrementIfOdd(value,onIncrement) } }> incrementIfOdd </button> <button onClick={ e=>{ incrementAsync(onIncrement) } }> incrementAsync </button> </p> ) }
注意 onClick裏面不能直接{incrementifOdd(value,onIncrement)}
問題:
深刻理解在 Redux 中 state 的更新與組件是如何共同運做的例子。展現了 reducer 如何委派 action 給其它 reducer,也展現瞭如何使用 React Redux 從展現組件中生成容器組件。
要點
import React from 'react' import { connect } from 'react-redux' import { addTodo } from '../actions' let AddTodo = ({ dispatch }) => { let input return ( <div> <form onSubmit={e => { e.preventDefault() if (!input.value.trim()) { return } dispatch(addTodo(input.value)) input.value = '' }}> <input ref={node => { input = node }} /> <button type="submit"> Add Todo </button> </form> </div> ) } AddTodo = connect()(AddTodo) export default AddTodo
比較這兩個寫法:具體看api使用:FilterLink.js裏的定義是一個函數,VisibleTodoList裏的定義是一個對象。
比較FilterLink.js和VisibileTodoList.js的mapDispatchToProps寫法,注意二者的區別FilterLink是屬性在標籤裏定義了。因此:
const mapDispatchToProps = (dispatch,ownProps) => ({ onClick:()=>{ dispatch(setVisibilityFilter(ownProps.filter)) } })
而VisibleTodoList的是從內部去跑這個方法的。
const mapDispatchToProps = { onTodoClick: toggleTodo }
另外,參考undo裏面有一個寫法:這也是傳入id來處理的。
const mapDispatchToProps = (dispatch) => { return { onTodoClick: (id) => { dispatch(toggleTodo(id)) } } }
參數傳遞在Todo裏面
const todoList = ({todos,onTodoClick})=>{ return (<ul> {todos.map(todo => <Todo key={todo.id} {...todo} onClick={()=> onTodoClick(todo.id)} /> )} </ul>) }
要點:
import React, { Component, PropTypes } from 'react' import classnames from 'classnames' export default class TodoTextInput extends Component { static propTypes = { onSave: PropTypes.func.isRequired, text: PropTypes.string, placeholder: PropTypes.string, editing: PropTypes.bool, newTodo: PropTypes.bool } state = { text: this.props.text || '' } handleSubmit = e => { const text = e.target.value.trim() if (e.which === 13) { this.props.onSave(text) if (this.props.newTodo) { this.setState({ text: '' }) } } } handleChange = e => { this.setState({ text: e.target.value }) } handleBlur = e => { if (!this.props.newTodo) { this.props.onSave(e.target.value) } } render() { return ( <input className={ classnames({ edit: this.props.editing, 'new-todo': this.props.newTodo })} type="text" placeholder={this.props.placeholder} autoFocus="true" value={this.state.text} onBlur={this.handleBlur} onChange={this.handleChange} onKeyDown={this.handleSubmit} /> ) } }
這種寫法,內部保持一個state,須要state通常是須要和用戶進行交互,改寫onKeyDown源文件以下函數組件:
import React from 'react' import classnames from 'classnames' /*const handleSubmit =(e)=>{ const text = e.target.value.trim() if(e.which === 13) { } } const handleBlur = (e)=>{ console.log(this.props) }*/ const TodoTextInput =({...props})=> <input type="text" className={ classnames({ edit: props.editing, 'new-todo': props.newTodo })} autoFocus="true" /* onBlur={handleBlur}*/ onKeyDown={(e)=>{ const text = e.target.value.trim() if(e.which === 13&&text!=='') { props.onSave(text) e.target.value="" } }} placeholder={props.placeholder}/> export default TodoTextInput
注意這裏onKeyDown裏面要引用外部的props,因此不能抽取到外面handleSubmit方法。會報props找不到。換個思路,若是使用連接工廠的方式:
onBlur={handleBlur(props)} ----------------------------------------------------- const handleBlur = (props)=>(e)=>{ //連接工廠的方式 console.log(props,e,e.target.value) }
這樣也是能夠的。或者換個思路
onBlur={e=>{handleBlur(props,e)}} ----------------------------------------- const handleBlur = (props,e)=>{ console.log(props,e,e.target.value) }
函數調用。通常下面的方法,若是隻寫一個textxxx那麼textxxx均可以調用的到,若是這樣寫了,根據就近原則,內部函數先調用了。
import React from 'react' import TodoTextInput from './TodoTextInput' const textsss = ()=>{ console.log("xx") } const Header = ({addTodo}) => { const textsss = ()=>{ console.log("xxwww") } //這裏如何定義內部方法。 return (<header className="header"> <h1>todos</h1> <TodoTextInput newTodo onSave={(text)=>{ textsss() addTodo(text) }} placeholder="what need to be done?" /> </header>) } export default Header
const TODO_FILTERS = { [SHOW_ALL]: () => true, [SHOW_ACTIVE]: todo => !todo.completed, [SHOW_COMPLETED]: todo => todo.completed }
這裏[]:xxx,[]裏面的是一個變量。
reduces/todos.js 裏面的complete裏面的一個寫法:對象擴展運算符,...todo,就是把對象擴展了。而後後面的屬性覆蓋前面的。
case types.COMPLETE_TODO: return state.map(todo=>{ return todo.id === action.id? {...todo,completed:!todo.completed}: todo })
能夠參考這篇文章。
export const fetchPostsIfNeeded = reddit => (dispatch, getState) => { debugger if (shouldFetchPosts(getState(), reddit)) { return dispatch(fetchPosts(reddit)) } }
這裏getState是從哪裏注入進來的?注意這裏調用方是
componentDidMount() { const { dispatch, selectedReddit } = this.props dispatch(fetchPostsIfNeeded(selectedReddit)) }
dispatch(fetchPostsIfNeeded(selectedReddit))
就是說只有注入dispatch,觸發dispatch,纔有getState()這個變量。讓咱們能夠作下一步的操做。能夠換成下面的代碼塊。
export const fetchPostsIfNeeded = reddit =>{ return (dispatch, getState) => { if (shouldFetchPosts(getState(), reddit)) { return dispatch(fetchPosts(reddit)) } } }
dispatch(fetchPostsIfNeeded(selectedReddit))
調用的時候,最後返回的是一個函數。能夠有這兩個變量。咱們修改todomvc 例子裏面的action/index.js,加入dispatch,getState(),以下,注意,若是想改爲這樣的話,
export const addTodo = text =>(dispatch,getState)=> {debugger;return ({ type: types.ADD_TODO, text })}
必須加入中間件。發現這是系統調用的時候調用的時候自動加入的。這也能夠理解,actioncreator原本是要返回一個plain的obj,不過如今須要作其餘的操做(網絡請求),就須要中間件。按照正常的邏輯,走網絡請求獲得數據應該要走網絡請求,因此函數的參數,就是狀態(getState)和動做(dispatch)。繼續作下一步的處理
使用thunk中間件是由於actionCreator不只僅要返回一個plain對象,還要在函數裏面作一些操做。好比異步請求,而後最後返回的是一個函數。
當 action creator 返回函數時,這個函數會被 Redux Thunk middleware 執行。這個函數並不須要保持純淨;它還能夠帶有反作用,包括執行異步 API 請求。這個函數還能夠 dispatch action,就像 dispatch 前面定義的同步 action 同樣。
比較這兩種寫法:
productContainer
<ProductsList title="Products"> {products.map(product => <ProductItem key={product.id} product={product} onAddToCartClicked={() => addToCart(product.id)} /> )} </ProductsList>
productItem裏面:
<button onClick={onAddToCartClicked} disabled={product.inventory > 0 ? '' : 'disabled'}> {product.inventory > 0 ? 'Add to cart' : 'Sold Out'} </button>
這種寫法把點擊事件的具體操做放到外層容器處理。
另外一種寫法是:
productContainer
<ProductsList title={"shoppingcart"}> {products.map(product=><ProductItem key={product.id} product={product} addToCart={addToCart}/>)} </ProductsList>
productItem裏面作處理:
export default ({product,addToCart}) => ( <div> <Product title={product.title} price={product.price} /> <button onClick={()=>addToCart(product.id)} disabled={product.inventory > 0 ? '' : 'disabled'}> {product.inventory > 0 ? 'Add to cart' : 'Sold Out'} </button> </div>)
在productItem裏面onClick另外處理,處理的場景不同而已。主要是看業務須要分割到什麼樣的程度。
注意如下這兩種寫法:
actions.js
export const testConsoleProductUnderirect = ()=>dispatch=>{ dispatch({ type:types.TEST_CONSOLE_PRODUCTS }) } export const testConsoleProductInderirect = ()=>({ type:types.TEST_CONSOLE_PRODUCTS })
調用方式爲
store.dispatch(testConsoleProductUnderirect())
store.dispatch(testConsoleProductInderirect())
第二種是常規的寫法,就是返回一個普通的action對象。第一種寫法是要由thunk中間件來觸發的寫法。注意
const xx=()=>dispatch=>({XX:XX})
每一層 箭頭都是有()包裹,是直接返回的。第一個xx=()=>dispatch
說明是返回一個參數爲dispatch的函數,通常actionCreator是要返回action對象的,這裏返回了一個函數,須要用到thunk支持,第二層箭頭>dispatch=>{ dispatch({ type:types.TEST_CONSOLE_PRODUCTS }) }
裏面就能夠dispatch其餘操做。根據中間件文檔的解釋,中間件處理過程以下。傳入action,而後返回next----》dispatch這個action,而後再store
這個next
store => next => action
層層嵌套。
注意使用了中間件以後,第二種actionCreator能夠返回任意東西。只要有dispatch參數進去就能夠了。
export const testConsoleProductUnderirect = ()=>dispatch=>{ console.log("hello kitty") /*dispatch({ type:types.TEST_CONSOLE_PRODUCTS })*/ }
第一種同步actionCreator不行
export const testConsoleProductInderirect = ()=>{ console.log("hello kitty") }
這樣寫是會報錯的。