天天看點

react+redux實戰——redux到底是個啥子?寫在前面三、分步操作寫在最後

寫在前面

文章是個人初學redux的分享,從redux基礎知識點到使用redux搭建簡單的表單送出項目。

項目代碼位址:https://github.com/jishuaizhen/react-redux-simple-project.git

項目學習視訊:https://ke.qq.com/course/368915

一、redux到底是個啥子?

1.redux和vuex具有類似的作用,為了友善元件間的資料通信而産生。

2.簡單了解:redux和vuex都提供了一個資料倉庫,元件無法直接修改倉庫中的資料,需要使用redux中提供的方法傳參修改或者擷取資料。

3.注意:redux涉及到三個比較重要的點:store、action、reducer;另外,在使用redux的時候參數也是傳來傳去的,比較麻煩,容易繞暈。

4.redux的詳細介紹:阮一峰老師的部落格http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html

5.

react+redux實戰——redux到底是個啥子?寫在前面三、分步操作寫在最後

在圖中可以看到,使用redux之後:1.使用者觸發元件中的事件,需要送出或者擷取資料;2.action接到使用者的操作并派發;3.action在派發過程會經過store,攜帶store中的資料和action(具體操作)提供給reducer;4.reducer處理之後傳回最新的狀态(也就是資料),更新store中的資料;5.當store發生變化之後,元件擷取資料進行頁面渲染。

二、react+redux實戰

1.這裡是我學習練習的一個小案例,架構使用create-react-app,請求的接口使用http://jsonplaceholder.typicode.com/posts

2.頁面布局

react+redux實戰——redux到底是個啥子?寫在前面三、分步操作寫在最後

頁面上方是添加标題和内容的文本框,頁面下方是請求接口的資料

3.主檔案App.js

react+redux實戰——redux到底是個啥子?寫在前面三、分步操作寫在最後

元件檔案在components檔案夾中,actions負責操作分發,reducers負責資料的處理,store.js負責

三、分步操作

1.Posts.js元件

fetch('http://jsonplaceholder.typicode.com/posts')
    .then(res=>res.json())
    .then(posts=>{
    	this.setState({
			posts:posts
		})
    })
    //通過請求擷取資料渲染到頁面
           

2.PostsForm.js元件

//渲染功能
render() {
    return (
      <div>
        <h1>添加内容</h1>
        <form onSubmit={this.onSubmit}>
            <div>
                <label>title</label>
                <br/>
                <input type="text" name="title" onChange={this.onChange} value={this.state.title}></input>
            </div>
            <div>
                <label>body</label>
                <br/>
                <textarea name="body" onChange={this.onChange} value={this.state.body}></textarea>
            </div>
            <br/>
            <button type="submit">添加</button> 
        </form>
      </div>
    )
  }
  ...
 //綁定函數
 onChange=(e)=>{
      console.log(e.target)
      this.setState({
        [e.target.name]:e.target.value
      })
    }
    onSubmit=(e)=>{
      e.preventDefault()
      const post = {
        title:this.state.title,
        body:this.state.body
      }
    }
   //資料請求
   fetch('http://jsonplaceholder.typicode.com/posts',{
        method:'POST',
        header:{
          "content-type":"application/json"
        },
        body:JSON.stringify(postData)
      })
      .then(res=>res.json())
      .then(data=>{console.log(data)}
        )
           

3.初識store

1.npm i redux react-redux redux-thunk
2.使用redux是為了狀态(資料)的統一管理,為了讓所有元件拿到狀态,需要使用<Provider></Provider>将根元件包裹
import {Provider} from 'react-redux'

function App() {
  return (
  <Provider store={store}>
    <div className="App">
      <PostForm></PostForm>
      <Posts></Posts>
    </div>
  </Provider>
  );
}
3.建立store, Redux 應用隻有一個單一的 store。當需要拆分資料處理邏輯時,你應該使用 reducer 組合而不是建立多個 store

import {createStore,applyMiddleware,compose} from 'redux'
import thunk from 'redux-thunk'
import rootReducer from './reducers/index'
const initialState = {}
const middleware = [thunk]
//createStore可以傳三個參數:1.多個reducer對象;2.初始狀态為空對象,當調用redux之後會傳回一個新的對象;3.中間件applyMiddlewares是 Redux 的原生方法,作用是将所有中間件組成一個數組,依次執行
export const store = createStore(
    rootReducer,
    initialState,
    applyMiddleware(...middleware)
)
           

4.Reducer和CombineReducers

1.Reducer
Store 收到 Action 以後,必須給出一個新的 State,這樣 View 才會發生變化。這種 State 的計算過程就叫做 Reducer。Reducer 是一個函數,它接受 Action 和目前 State 作為參數,傳回一個新的 State。

export default function(state=initialState,action){
    switch(action.type){
        default:
            return state;
    }
}

2.Reducer 的拆分
Reducer 函數負責生成 State。由于整個應用隻有一個 State 對象,包含所有資料,對于大型應用來說,這個 State 必然十分龐大,導緻 Reducer 函數也十分龐大,是以需要拆分Reducer。

//在創造store過程中rootReducer是多個對象
export const store = createStore(
    rootReducer,
    initialState,
    applyMiddleware(...middleware)
)

3.CombineReducers
這樣一拆,Reducer 就易讀易寫多了。而且,這種拆分與 React 應用的結構相吻合:一個 React 根元件由很多子元件構成。這就是說,子元件與子 Reducer 完全可以對應。
Redux 提供了一個combineReducers方法,用于 Reducer 的拆分。你隻要定義各個子 Reducer 函數,然後用這個方法,将它們合成一個大的 Reducer。

import {combineReducers} from 'redux'
import postReducer from './postReducer'

export default combineReducers({
    posts:postReducer
})
           

5.actions和types

1.我們需要在actions檔案夾裡面定義操作的函數,然後在元件内進行調用。

重點:這一步就是将原本存在在元件中的操作(資料請求等)轉移到actions中。元件調用actions中的方法就需要使用connect建立連結,React-Redux 提供connect方法,用于從 UI 元件生成容器元件。connect的意思,就是将這兩種元件連起來。

import { connect } from 'react-redux'
import { fetchPosts } from '../actions/postActions'

const mapStateToProps = state => ({
  posts:state.posts.items,
  newPost:state.posts.item
})
export default connect(mapStateToProps,{fetchPosts})(Posts);

2.現在我們可以通過元件調用actions裡面的方法

重點:redux需要actions的dispatch分發操作攜帶方法和store中的狀态交給reducer進行處理,reducer傳回更新後的狀态(資料),元件進行更新。actions中有多種操作,是以在dispatch分發操作時需要配置action的types屬性進行分辨執行哪個操作。

//建立types.js檔案,types檔案就是為不同action定義不同名字,需要在actions和reducers檔案中引入,以便讓reducer知道解決哪一種操作
export const FETCH_POSTS = "FETCH_POSTS";
export const NEW_POSTS = "NEW_POSTS";
3.
//postActions.js檔案
export const fetchPosts = () => dispatch => {
    fetch('http://jsonplaceholder.typicode.com/posts')
    .then(res=>res.json())
    .then(posts=>
    // 當操作成功時,通過dispatch攜帶action.type和請求到資料給store,store告訴postReducer.js進行操作,傳回處理後的狀态
        dispatch({
            type:FETCH_POSTS,
            payload:posts
        })
    )
}
//postReducer.js
export default function(state=initialState,action){
    switch(action.type){
        case NEW_POSTS:
            return {
                ...state,
                item:action.payload
            }
        case FETCH_POSTS:
            return {
                ...state,
                items:action.payload
            }
        default:
            return state;
    }
}

           

6.mapState擷取最新狀态

1.postReducer.js接收到action.type和請求到資料進行處理傳回新的狀态,這裡reducer處理action操作得到的資料進行處理傳回新的資料給store。

2.store中的資料更新,元件需要想辦法進行接收。
mapStateToProps()是一個函數。它的作用就是像它的名字那樣,建立一個從(外部的)state對象到(UI 元件的)props對象的映射關系。

const mapStateToProps = state => ({
  posts:state.posts.items
})
export default connect(mapStateToProps,{fetchPosts})(Posts);

3.另外redux要求我們需要給方法和狀态規定資料類型
import PropTypes from 'prop-types'
//使用方法
Posts.proptypes = {
  fetchPosts:PropTypes.func.isRequired,
  posts:PropTypes.array.isRequired
}
           

7.添加資料

1.接收到文本框的資料PostForm.js
onSubmit=(e)=>{
      e.preventDefault()
      const post = {
        title:this.state.title,
        body:this.state.body
      }
      // 觸發action
      this.props.createPost(post)
    }
 2.PostActions.js接收到操作通知
 export const createPost = postData => dispatch => {
    fetch('http://jsonplaceholder.typicode.com/posts',{
        method:'POST',
        header:{
          "content-type":"application/json"
        },
        body:JSON.stringify(postData)
      })
      .then(res=>res.json())
      .then(post=>
        // 當操作成功時,通過dispatch将資料和action.type交給postReducer.js處理
        dispatch({
            type:NEW_POSTS,
            payload:post
        })
        )
}
3.postReducer.js根據對應的操作傳回處理後的資料
export default function(state=initialState,action){
    switch(action.type){
        case NEW_POSTS:
            return {
                ...state,
                item:action.payload
            }
        case FETCH_POSTS:
            return {
                ...state,
                items:action.payload
            }
        default:
            return state;
    }
}
4.資料更改後,Post.js接收資料進行渲染
const mapStateToProps = state => ({
  posts:state.posts.items,
  newPost:state.posts.item //這裡是添加的資料
})
           

寫在最後

1.自認為redux比vuex複雜些,操作和資料類型要求的更規範些

2.官方将store中的資料定義為狀态,狀态改變即為資料更新,是以文章中有些地方使用狀态和資料兩種說明

3.使用redux後操作和資料請求不在元件中進行,而是交給actions和reducers,reducers将資料處理後傳回給store,store通過mapStateToProps()方法将store中的資料對象和UI元件中的props進行映射,props改變導緻頁面進行重新渲染

建議:個人是初學者,很多地方描述不太準确。大家可以先看一下學習視訊和阮一峰老師的部落格,然後可以看一下我的文章,有什麼不準确的地方可以留言說明。