redux 學習

參考資料

Awesome Redux 包含文檔,redux項目,以及一些腳手架工具html

reactjs101node

full stack redux tutorialreact

慕課網git

example

the-soundcloud-client-in-react-reduxgithub

中間件middleware

參考資料:npm

redux middleware 詳解json

example

觀看example的時候,要把整個脈絡action--》reducer---》store---》搞清楚,好比reducer要書寫,參考(action--》reducer這條線)因此須要action這個參數入侵。好比store要書寫,須要reducer--》store,因此須要reducer入侵。redux

代碼的編寫參考例子。api

故障分析

有時候cnpm不起做用,多是被360攔截了。數組

添加信任就能夠了。

 

官網

Counter Vanilla

一個入門demo,描述了redux如何運做的過程,從action--》reducer---》store---》更新應用state的過程。

  • store:
    •  store.dispatch({ type: 'xxx' }),這裏store分發dispatch(觸發)這些action,回調subscribe由store訂閱  store.subscribe(render)好了
  • reducer:
    • fn:counter(state,action)是reducer,根據action.type,獲得不一樣的狀態。
    • var store = Redux.createStore(counter),這裏store就和reducer綁定在一塊兒了。
  • action
    • { type: 'xxx' }描述一種行爲。行爲具體怎麼發生在reducer(這裏是counter函數)裏面定義了。

注意點:redux採用的是消費訂閱者模式。

Counter

commonjs方式的demo。簡易版的demo,方便了解整個rudux過程。

Redux 結合 React 使用的最基本示例。出於簡化,當 store 發生變化,React 組件會手動從新渲染。在實際的項目中,可使用 React 和 Redux 已綁定、且更高效的 React Redux

要點

  • reactjs組件不須要知道redux,只是在外層導入props的時候 onIncrement={() => store.dispatch({type:'INCREMENT'})}關聯store,讓store去dispatch相應的動做,根據subscribe到store的reducer去判斷類型,進行操做。這也是redux的底層的操做。
  • 組件和redux相關的組件書寫的時候不會相互影響,能夠分開編寫,測試。
  • couter組件不包含狀態,可使用另外一種寫法,不使用class的方式
    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)}

問題:

  • package.json文件"react-scripts": "^0.6.0",這個文件的做用(好像是簡化測試,以及發佈的模塊),配合這個庫使用的要加入react-addons-test-utils,enzyme這兩個模塊。

Todos

深刻理解在 Redux 中 state 的更新與組件是如何共同運做的例子。展現了 reducer 如何委派 action 給其它 reducer,也展現瞭如何使用 React Redux 從展現組件中生成容器組件。

要點

  • container的都是要跟redux關聯的組件,也就是容器組件的做用,從redux的state樹中取得本身要的數據mapStateToProps和動做mapDispatchToProps(要響應的dispath),關聯react展現組件。而後react組件從這些容器中取得相應的數據和動做進行處理。
  • 組件觸發動做action(帶有數據)的時候,redux會去遍歷全部的reduce,而後找到匹配的reduce,而後redux會自動更新state,react會更新視圖。
  • 容器組件能夠用動詞來表達,好比addTodo,VisibleTodoList,而展現組件能夠是一個名詞好比footer等。
  • 注意容器組件AddTodo的寫法。沒有寫mapStateToProps,這些東西。參考connect用法
    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>)
    }

     

todomvc

要點:

  • 改寫官網TodoTextInput.js源文件以下:組件繼承方式
    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

     

  • 注意看下面的定義對象的方式 mainsection裏面: 
    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
    					})

     

async

能夠參考這篇文章

  • 注意代碼段,
    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 同樣。

shopping

比較這兩種寫法:

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")
}

這樣寫是會報錯的。

相關文章
相關標籤/搜索