redux 使用

2017-12-05  本文已影响17人  日不落000

package.json

"dependencies": {
    "react-redux": "^5.0.6",
    "redux": "^3.7.2",
    "redux-thunk": "^2.2.0",
}

"devDependencies": {
    "remote-redux-devtools": "^0.5.12",
    "remote-redux-devtools-on-debugger": "^0.8.3",
  },
import { Provider } from "react-redux";
import store from "./store/store.js";

<Provider store={store}>
    <MyRootComponent />
  </Provider>

./store/store.js

/**
 * Created by nick on 2017/12/4.
 */
import allReducers from "../reducers/index.js";

import { applyMiddleware, createStore } from "redux";
/**
 * Logs all actions and states after they are dispatched.
 */
const logger = store => next => action => {
    console.group(action.type);
    console.info('dispatching', action);
    let result = next(action);
    console.log('next state', store.getState());
    console.groupEnd(action.type);
    return result;
};

/**
 * Sends crash reports as state is updated and listeners are notified.
 */
// const crashReporter = store => next => action => {
//     try {
//         return next(action)
//     } catch (err) {
//         console.error('Caught an exception!', err)
//         Raven.captureException(err, {
//             extra: {
//                 action,
//                 state: store.getState()
//             }
//         })
//         throw err
//     }
// }

/**
 * Schedules actions with { meta: { delay: N } } to be delayed by N milliseconds.
 * Makes `dispatch` return a function to cancel the timeout in this case.
 */
const timeoutScheduler = store => next => action => {
    if (!action.meta || !action.meta.delay) {
        return next(action);
    }

    let timeoutId = setTimeout(
        () => next(action),
        action.meta.delay
    );

    return function cancel() {
        clearTimeout(timeoutId);
    };
};

/**
 * Schedules actions with { meta: { raf: true } } to be dispatched inside a rAF loop
 * frame.  Makes `dispatch` return a function to remove the action from the queue in
 * this case.
 */
const rafScheduler = store => next => {
    let queuedActions = [];
    let frame = null;

    function loop() {
        frame = null;
        try {
            if (queuedActions.length) {
                next(queuedActions.shift());
            }
        } finally {
            maybeRaf();
        }
    }

    function maybeRaf() {
        if (queuedActions.length && !frame) {
            frame = requestAnimationFrame(loop);
        }
    }

    return action => {
        if (!action.meta || !action.meta.raf) {
            return next(action);
        }

        queuedActions.push(action);
        maybeRaf();

        return function cancel() {
            queuedActions = queuedActions.filter(a => a !== action);
        };
    };
};

/**
 * Lets you dispatch promises in addition to actions.
 * If the promise is resolved, its result will be dispatched as an action.
 * The promise is returned from `dispatch` so the caller may handle rejection.
 */
const vanillaPromise = store => next => action => {
    if (typeof action.then !== 'function') {
        return next(action);
    }

    return Promise.resolve(action).then(store.dispatch);
};

/**
 * Lets you dispatch special actions with a { promise } field.
 *
 * This middleware will turn them into a single action at the beginning,
 * and a single success (or failure) action when the `promise` resolves.
 *
 * For convenience, `dispatch` will return the promise so the caller can wait.
 */
const readyStatePromise = store => next => action => {
    if (!action.promise) {
        return next(action);
    }

    function makeAction(ready, data) {
        let newAction = Object.assign({}, action, { ready }, data);
        delete newAction.promise;
        return newAction;
    }

    next(makeAction(false));
    return action.promise.then(
        result => next(makeAction(true, { result })),
        error => next(makeAction(true, { error }))
    );
};

/**
 * Lets you dispatch a function instead of an action.
 * This function will receive `dispatch` and `getState` as arguments.
 *
 * Useful for early exits (conditions over `getState()`), as well
 * as for async control flow (it can `dispatch()` something else).
 *
 * `dispatch` will return the return value of the dispatched function.
 */
const thunk = store => next => action =>
    typeof action === 'function'
        ? action(store.dispatch, store.getState)
        : next(action);

// You can use all of them! (It doesn't mean you should.)

let store = createStore(
    allReducers,
    applyMiddleware(
        rafScheduler,
        timeoutScheduler,
        thunk,
        vanillaPromise,
        readyStatePromise,
        logger
    )
);

module.exports = store;

todoActionCreators.js

/**
 * Created by nick on 2017/12/4.
 */
export const ADD_TODO = 'ADD_TODO';
export const SET_INPUT_TEXT = 'SET_INPUT_TEXT';

/*
 * action creators
 */

export function addTodo(text) {
    return { type : ADD_TODO, text };
}

export function setInputText(text) {
    return { type : SET_INPUT_TEXT, text };
}

//todo async

export const REQUEST_POSTS = 'REQUEST_POSTS';
function requestPosts(subreddit) {
    return {
        type : REQUEST_POSTS,
        subreddit
    };
}

export const RECEIVE_POSTS = 'RECEIVE_POSTS';
function receivePosts(subreddit, json) {
    return {
        type : RECEIVE_POSTS,
        subreddit,
        posts : json.data.children.map(child => child.data),
        receivedAt : Date.now()
    };
}

// 来看一下我们写的第一个 thunk action creator!
// 虽然内部操作不同,你可以像其它 action creator 一样使用它:
// store.dispatch(fetchPosts('reactjs'))

export function fetchPostsOld(subreddit) {

    // Thunk middleware 知道如何处理函数。
    // 这里把 dispatch 方法通过参数的形式传给函数,
    // 以此来让它自己也能 dispatch action。

    return function (dispatch) {

        // 首次 dispatch:更新应用的 state 来通知
        // API 请求发起了。

        dispatch(requestPosts(subreddit))

        // thunk middleware 调用的函数可以有返回值,
        // 它会被当作 dispatch 方法的返回值传递。

        // 这个案例中,我们返回一个等待处理的 promise。
        // 这并不是 redux middleware 所必须的,但这对于我们而言很方便。

        return fetch(`http://www.subreddit.com/r/${subreddit}.json`)
            .then(response => response.json())
            .then(json =>

                // 可以多次 dispatch!
                // 这里,使用 API 请求结果来更新应用的 state。

                dispatch(receivePosts(subreddit, json))
            )

        // 在实际应用中,还需要
        // 捕获网络请求的异常。
    }
}

export function fetchPosts(subreddit) {

    // Thunk middleware 知道如何处理函数。
    // 这里把 dispatch 方法通过参数的形式传给函数,
    // 以此来让它自己也能 dispatch action。

    // OtherApi.postInfoNormalNoAccessTokenTestRedux(`http://www.subreddit.com/r/${subreddit}.json`,{},(jsonObj)=>{
    //     console.log(jsonObj);
    // })
    //
    // return;

    return async function (dispatch) {

        // 首次 dispatch:更新应用的 state 来通知
        // API 请求发起了。

        dispatch(requestPosts(subreddit));

        // thunk middleware 调用的函数可以有返回值,
        // 它会被当作 dispatch 方法的返回值传递。

        // 这个案例中,我们返回一个等待处理的 promise。
        // 这并不是 redux middleware 所必须的,但这对于我们而言很方便。

        // return fetch(`http://www.subreddit.com/r/${subreddit}.json`)
        //     .then(response => response.json())
        //     .then(json =>
        //
        //         // 可以多次 dispatch!
        //         // 这里,使用 API 请求结果来更新应用的 state。
        //
        //         dispatch(receivePosts(subreddit, json))
        //     );
        //

        let responseJson = await postAsync(`http://www.subreddit.com/r/${subreddit}.json`, {}, subreddit, dispatch)

        dispatch(receivePosts(subreddit, responseJson))

        return;

        // 在实际应用中,还需要
        // 捕获网络请求的异常。
    };
}

async function postAsync(url, bodyObject, subreddit, dispatch) {
    let headers = {
        'Accept' : 'application/json',
        'Content-Type' : 'application/json',
    };

    let response
        = await
        fetch(url, {
            method : 'POST',
            headers : headers,
            body : JSON.stringify(bodyObject),
        });
    console.log(response);

    let responseJson
        = await
        response.json();
    console.log(responseJson);
    // console.log(StringUtil.object2Json(responseJson));

    return responseJson;

};


./reducers/todoReducer.js

/**
 * Created by nick on 2017/12/5.
 */
import { combineReducers } from "redux";
import { ADD_TODO, RECEIVE_POSTS, SET_INPUT_TEXT, } from "../actions/todoActionCreators";

function header_input_value(state = 'header_input_value', action) {
    switch (action.type) {
        case SET_INPUT_TEXT:
            return action.text;
        default:
            return state;
    }
}

function receivePost(state = {}, action) {
    switch (action.type) {
        case RECEIVE_POSTS:
            return action;
        default:
            return state;
    }
}

function todos(state = [ { text : "Use Redux11", completed : false } ], action) {
    switch (action.type) {
        case ADD_TODO:
            return [
                ...state,
                {
                    text : action.text,
                    completed : false
                }
            ];
        case TOGGLE_TODO:
            return state.map((todo, index) => {
                if (index === action.index) {
                    return Object.assign({}, todo, {
                        completed : !todo.completed
                    });
                }
                return todo;
            });
        default:
            return state;
    }
}

const todoApp = combineReducers({
    header_input_value,
    receivePost,
    todos
});

export default todoApp;

./reducers/index.js

import { combineReducers } from "redux";
import todoReducer from "./todoReducer.js";

const allReducers = combineReducers({
    todoReducer,
});

export default allReducers;

ZZTestReduxTodoListScreen.js

/**
 * Created by nick on 2017/4/20.
 * https://redux.js.org/docs/basics/ExampleTodoList.html
 */
import React, { Component } from "react";
import { Container, Content } from "native-base";
import ConstantUtil from "../utils/ConstantUtil";
import { InteractionManager, Text, View } from "react-native";
import BaseCommon from "../common/BaseCommon";
import HeaderNormalWithRightButtonAndSearch from "../components/HeaderNormalWithRightButtonAndSearch";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import * as todoActionCreators from "../actions/todoActionCreators";
import MyButton from "../components/MyButton";
import * as StyleUtil from "../utils/StyleUtil";

class TodoList extends Component {
    render() {
        let { todos } = this.props;
        let _view = null;

        _view = (
            <View>
                {todos && todos.length > 0 ? todos.map(
                    (item, i) => {
                        return (
                            <Text key={`key_${i}`}>{item.text}</Text>
                        );
                    }
                )
                    : null
                }
            </View>
        );
        return _view;

    }
}

class ZZTestReduxTodoListScreen extends Component {

    static propTypes = {
        header_title : React.PropTypes.string,       //

    };

    static defaultProps = {
        header_title : '',
    };


    constructor(props) {
        super(props);
        const { dispatch } = props;

        // Here's a good use case for bindActionCreators:
        // You want a child component to be completely unaware of Redux.
        // We create bound versions of these functions now so we can
        // pass them down to our child later.

        this.boundActionCreators = bindActionCreators(todoActionCreators, dispatch)
        console.log(this.boundActionCreators)
        // {
        //   addTodo: Function,
        //   removeTodo: Function
        // }


        this.state = {
        };
    }

    componentDidMount() {
        console.log(this);
        let { actions } = this.props

        // Note: this won't work:
        // TodoActionCreators.addTodo('Use Redux')

        // You're just calling a function that creates an action.
        // You must dispatch the action, too!

        // This will work:
        actions.addTodo('Use Redux')

    }

    componentWillMount() {
    }

    componentWillUnmount() {
    }

    componentWillReceiveProps(nextProps) {
    }

    render() {

        let { header_input_value, receivePost } = this.props;
        let { setInputText, fetchPosts } = this.props.actions;

        let _viewHeader = null;
        _viewHeader = (
            <HeaderNormalWithRightButtonAndSearch
                _rightBtnShouldShow={true}
                _textBtn={'添 加'}
                _onPressBtn={() => {
                    let { actions } = this.props

                    // Note: this won't work:
                    // TodoActionCreators.addTodo('Use Redux')

                    // You're just calling a function that creates an action.
                    // You must dispatch the action, too!

                    // This will work:
                    actions.addTodo(header_input_value)
                }}

                _inputPlaceHolder={this.props.header_input_place_holder}
                _inputDefaultValue={header_input_value}
                _inputValue={header_input_value}
                _onChange={(value) => {

                    setInputText(value);

                }}
            />
        );
        let { todos } = this.props

        return (
            <Container style={{ backgroundColor : ConstantUtil.colors.bgGray }}>

                {_viewHeader}

                <Content
                    keyboardShouldPersistTaps='always'
                    // keyboardDismissMode={'on-drag'}
                    showsVerticalScrollIndicator={false}

                    contentContainerStyle={{
                        justifyContent : 'center',
                        alignItems : 'stretch',
                        flex : 1,
                    }}>

                    <MyButton
                        textStyle={[ StyleUtil.gStyles.gButtonTextWhiteDefault, ]}
                        buttonStyle={[ StyleUtil.gStyles.gButtonBlueDefault, {}, ]}
                        onClick={() => {

                            fetchPosts(header_input_value);

                        }}
                    >
                        <Text> 网络请求 </Text>
                    </MyButton>

                    <Text>{receivePost.receivedAt}</Text>
                    <Text style={{ width : StyleUtil.size.width, height : 100, }}>{JSON.stringify(receivePost)}</Text>

                    <TodoList todos={todos} {...this.boundActionCreators} />

                </Content>

            </Container>
        );
    }

}
function mapStateToProps(state) {
    return {
        todos : state.todoReducer.todos,
        header_input_value : state.todoReducer.header_input_value,
        receivePost : state.todoReducer.receivePost,
    };
}

function mapDispatchToProps(dispatch) {
    return { actions : bindActionCreators(todoActionCreators, dispatch) };
}

export default connect(mapStateToProps, mapDispatchToProps)(ZZTestReduxTodoListScreen);


re.
http://www.redux.org.cn/docs/advanced/AsyncActions.html

上一篇下一篇

猜你喜欢

热点阅读