redux 学习

前端之家收集整理的这篇文章主要介绍了redux 学习前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

参考资料

Awesome Redux包含文档,redux项目,以及一些脚手架工具

reactjs101

full stack redux tutorial

慕课网

example

the-soundcloud-client-in-react-redux

中间件middleware

参考资料:

redux middleware 详解

example

观看example的时候,要把整个脉络action--》reducer---》store---》搞清楚,比如reducer要书写,参考(action--》reducer这条线)所以需要action这个参数入侵。比如store要书写,需要reducer--》store,所以需要reducer入侵。

代码的编写参考例子。

故障分析

有时候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.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")
}

这样写是会报错的。

猜你在找的React相关文章