用react+redux編寫一個頁面小demo

初步目錄結構

整體目錄結構

這裏寫圖片描述

src目錄結構

這裏寫圖片描述

entry 存放頁面的入口文件

src 頁面的源文件

static 頁面源文件打包後生成的文件

webpack webpack打包文件

package.json package.json文件

.babelrc 支持es6語法

其中 src 中子目錄結構如下:

components 頁面組件

constants 頁面需要用到的一些常量

helpers 工具方法

mock 模擬json數據

redux redux數據控制目錄

views 頁面視圖文件,主要視圖全在這個文件

react配合redux編寫頁面流程

entry/hello.js

import React from 'react';
import { render } from 'react-dom';
import HelloPage from '../src/views/HelloPage';

import helloStore from '../src/redux/stores/helloStore';
import { Provider } from 'react-redux';

render(
    <Provider store={helloStore}>
        <HelloPage />
    </Provider>,
    document.getElementById('hello')
);

HelloPage.jsx是視圖文件,通過react-redux中的Providerstore綁定到視圖中

src/redux/actions/helloAction.js

import fetch from 'isomorphic-fetch';

// 純事件定義
export const ADD_COUNT = 'ADD_COUNT';
export const ADD_PERSON = 'ADD_PERSON';
export const DELETE_PERSON = 'DELETE_PERSON';

// async
// 異步的請求定義
export const FETCH_START = 'FETCH_START';
export const FETCH_SUCCESS = 'FETCH_SUCCESS';
export const FETCH_FAUILE = 'FETCH_FAUILE';

// pure functions
export function addCount() {
    return {
        type : ADD_COUNT
    }
}

export function addPerson(person) {
    return {
        type : ADD_PERSON,
        person
    }
}

export function deletePerson(idx) {
    return {
        type : DELETE_PERSON,
        idx
    }
}

export function refreshStart() {
    return {
        type : FETCH_START
    }
}

export function refreshSuccess(list) {
    return {
        type : FETCH_SUCCESS,
        list
    }
} 

export function refreshFauile() {
    return {
        type : FETCH_FAUILE
    }
}

// 定義的非純函數,提供異步請求支持
// 需要在sotre中使用thunkMiddleware
export function refresh() {
    return dispatch => {
        dispatch(refreshStart());
        return fetch(`src/mock/fetch-data-mock.json`)
            .then(response => response.json())
            .then(json => {
                setTimeout(() => {
                    dispatch(refreshSuccess(json && json.data.list));
                }, 3000);
            });
    }
}

action中主要定義事件類型,基本上都是一些常量。另外如果要進行一些非常量返回,比如異步請求,則需要輸出一個函數,這個函數通常帶有dispatch這個對象,用於對action的重新包裝,其實類似於後臺語言中的“攔截器”,返回函數之後,需要在store中配置thunkMiddleware

src/redux/reducers/helloReducers.js

import { combineReducers } from 'redux';
import { ADD_COUNT, ADD_PERSON, DELETE_PERSON, FETCH_START, FETCH_SUCCESS, FETCH_FAUILE } from '../actions/helloActions';

// store中可以定義頁面中的初始狀態
const initialState = {
    count : 0,      // count = 0
    loaded : false, // 異步請求是否加載
    personList : [  // 人員列表
        {"name" : "lily", "age" : 21}
    ]
};

// count的初始狀態以及處理之後返回的state值
function count(state = initialState.count, action) {
    switch (action.type) {
        case ADD_COUNT : 
            return state + 1;
        default : 
            return state;
    }
}

function personList(state = initialState, action) {
    switch (action.type) {
        case ADD_PERSON : 
            return Object.assign({}, ...state, {
                personList : [...state.personList, action.person]
            });
        case DELETE_PERSON :
            return Object.assign({}, ...state, {
                personList : state.personList.filter((s, i) => {
                    return action.idx !== i;
                })
            });
        case FETCH_START :
        case FETCH_SUCCESS : 
        case FETCH_FAUILE : 
            return fetchDataFromServer(state, action);
        default : 
            return state;
    }
}

function fetchDataFromServer(state, action) {
    if (action.type === FETCH_SUCCESS) {
        console.log(action);
        return Object.assign({}, ...state, {
            personList : [...state.personList, ...action.list],
            loaded : true
        });
    }
    return state;
}

const helloReducers = combineReducers({
    count,
    personList
});

export default helloReducers;

reducer中是對action發起的數據進行處理,這其中可能action只是發出了一個純指令,帶參數或者不帶參數根據業務的需求來定,總一個準則就是,初始的state經過處理之後會返回新的state。即:( state, action ) => newState。同時另外注意一點的是,初始的state值是不會變得,需要操作則會另外創建一個新的state,來保證某些場景的性能問題(因爲狀態的改變會導致頁面重新渲染,如果返回的state引用相同,則不會存在如此問題)。可以使用Immutable.js來保證state的純潔性。

src/redux/stores/helloStore.js

import { createStore, applyMiddleware } from 'redux';
import helloReducers from '../reducers/helloReducers';
import logger from '../middlewares/loggerMiddleWare';
import thunkMiddleware from 'redux-thunk';

// middleware可以自己定義,例如下面的logger
// 寫一個自定義的middleware遵循下面的格式:
// const logger = store => next => action => {
//  // what you do before action, 
//      // example: logger. console.log("dispatching", action);
//  let result = next(action);
//      // what you can do after action    
//  //console.log('next state', store.getState());
//  return result;
//}

let createStoreWithMiddleware = applyMiddleware(/*logger, */thunkMiddleware)(createStore);

export default createStoreWithMiddleware(helloReducers);

store中主要綁定從reducer返回的狀態

src/views/HelloPage.jsx

import React from 'react';
import { connect } from 'react-redux';

import { addCount, addPerson, deletePerson, refresh } from '../redux/actions/helloActions';
import HelloWorld from '../components/hello/HelloWorld';
import Button from 'react-bootstrap-myui/lib/Button';
import Table from 'react-bootstrap-myui/lib/Table';

const HelloPage = React.createClass({

    render () {
        let { count, list, loaded } = this.props;

        let personTbody = list.map((l, i) => (<tr key={"tr"+i}><td>{i}</td><td>{l.name}</td>
            <td>{l.age}</td><td><a href="javascript:;" onClick={this.deletePerson.bind(this, i)}>delete</a></td></tr>));

        return (
            <div id="reactPage">
                <HelloWorld />
                <Button onClick={this.addperson}>add person</Button>
                <Button onClick={this.refresh}>refresh</Button>
                {loaded ? (<span>加載完成</span>) : (<span>正在加載數據...</span>)}
                <Table striped hover>
                    <thead>
                        <tr>
                            <th>#</th>
                            <th>name</th>
                            <th>age</th>
                            <th>action</th>
                        </tr>
                    </thead>
                    <tbody>
                        {personTbody}
                    </tbody>
                </Table>
                <span>show me the current count : {count || 0}</span>
                <div><button onClick={this.addCount}>Add</button></div>
            </div>

        );
    },

    addCount() {
        let { dispatch, count } = this.props;
        dispatch(addCount());
    },

    addperson() {
        let { dispatch } = this.props;
        dispatch(addPerson({"name" : "tome", age : 25}));
    },

    deletePerson(index) {
        let { dispatch } = this.props;
        dispatch(deletePerson(index));
    }, 

    refresh() {
        let { dispatch } = this.props;
        dispatch(refresh());
    }

});

function select(state) {
    return {
        count : state.count,
        list : state.personList.personList,
        loaded : state.personList.loaded
    }
}

export default connect(select)(HelloPage);

詳細的頁面,在頁面中默認會帶有從store中返回的狀態,會以props的形式傳遞到頁面中,默認會帶有一個dispatch函數,用於發起action,因此這個action可以綁定頁面的一些業務邏輯操作,比如:“新增”、“刪除”、“修改”……

發佈了60 篇原創文章 · 獲贊 29 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章