美文网首页
redux 综合

redux 综合

作者: 细密画红 | 来源:发表于2018-11-12 10:38 被阅读12次

    1. what is state

    state influences what you see on the screen.

    2. the complexity of state management

    react is great at reacting to the state changes and updating the UI . But managing that state can get very difficult as our application grows.

    3. understanding the redux flow

    image.png

    4. Setting up reducer and store

    npm install --save redux
    
    const redux = require('redux');
    const createStore = redux.createStore;
    const initialState={
         counter: 0;
    };
    // Reducer
    const rootReducer = (state = initialState, action) => {
       if(action.type === 'ADD_COUNTER'){
            return {
              ...state,
               counter: state.counter + action.value
            };
       }
      return state;
    };
    // Store  
    //a store needs to be initialized with a reducer.
    // the reducer is strongly connected to the store.
    // it's the only thing that may update the state in the end.
    // that's why we need to pass the reducer to this 
    //creation function here because it's so cloesely // connected to the state.
    const store = createStore(rootReducer);
    console.log(store.getState());
    
    // Dispatching Action
    store.dispatch({type:'INC_COUNTER'});
    store.dispatch({type:'ADD_COUNTER',value: 10);
    console.log(store.getState());
    
    // Subscription
    // subscription make sure that I don't have to manually call getState function if I want to 
    // get the current state snapshot but to inform me whenever I need to get a new state because something changed.
    // subscribe takes a arg, a function which will be executed when ever the state is updated.
    store.subscribe(() => {
       console.log('SUBSCRIBE', store.getState());
    });
    

    5. Connecting react to redux

    npm install --save react-redux
    
    import React from 'react';
    import ReactDOM from 'react-dom';
    import { createStore } from 'redux';
    import  reducer from './store/reducer';
    import { Provider } from 'react-redux';
    import './index.css';
    import App from './App';
    
    const store = createStore(reducer);
    
    //  Provider is a helper component which allows us to kind of inject our store into the react components.
    ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root');
    

    创建一个store文件夹,存放reducer.js文件

    // reducer.js
    const initialState = {
        counter: 0
    }
    const reducer = (state = initialState, action) => {
        return state;
    }
    export default reducer;
    

    在组件里如何拿到 store?

    // 组件 counter.js
    ...
    // connect is not really a  higher order component.  it's a function which returns a higher order component.
    import { connect } from 'react-redux';
    ...
    class Counter extends Component {
      ...
    }
    const matStateToProps = state =>{
       return {
         ctr:state.counter
      };
    }
    const mapDispatchToProps = dispatch =>{
        return {
          onIncrecementCounter: () => dispatch({type:'INCREMENT'})
       }
    }
    export default connect(matStateToProps, mapDispatchToProps)(Counter);
    ...
    

    reducers重构

    // reducer.js
    const initialState = {
        counter: 0
    }
    const reducer = (state = initialState, action) => {
        switch( action.type ){
            case 'INCREMENT':
                return {
                    counter: state.counter + 1
               }
            case 'DECREMENT':
                  return {
                     counter: state.counter -1 
               }
        }
      return state;
    };
    export default reducer;
    

    6. Updating state Immutably

    7. Combining multiple reducers

    ...
    import { createStore, combineReducers } from 'redux';
    import counterReducer from './store/reducers/counter';
    import resultReducer from './store/reducers/result';
    
    const rootReducer = combineReducers({
       ctr: counterReducer,
       res: resultReducer
    });
    const store = createStore(rootReducer);
    ...
    

    引用store的地方需要修改(增加了命名空间)

    const matStateToProps = state =>{
       return {
        // 修改前代码:ctr:state.counter,增加了ctr命名空间
         ctr:state.ctr.counter
      };
    }
    

    Middleware

    image.png
    import React from 'react';
    import ReactDOM from 'react-dom';
    import { createStore, combineReducers, applyMiddleware } from 'redux';
    import counterReducer from './store/reducers/counter';
    import resultReducer from './store/reducers/result';
    import { Provider } from 'react-redux';
    import './index.css';
    import App from './App';
    
    // custom middleware
    const logger = store => {
       return next => {
           return action => {
               console.log('Middleware dispatching', action);
               const result = next(action);
               console.log('Middleware next state', store.getState());
               return result;
           }
       }
    };
    const rootReducer = combineReducers({
       ctr: counterReducer,
       res: resultReducer
    });
    
    // applyMiddleware allows us to add our own middleware to the store.
    const store = createStore(rootReducer, applyMiddleware(logger));
    
    ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root');
    

    the reducer function has to run synchronously.
    there is no way you can execute asynchronous code in reducer.

    some other way :

    1. execute asynchronous code with the help of so-called action creators.
    // actions.js
    export const INCREMENT='INCREMENT';
    export const DECREMENT='DECREMENT';
    ...
    const increment =()=>{
         return {
           type:INCREMENT
        }
    };
    
    
    // 在Counter组件中使用 action creator
    import { increment } from '../../store/actions/actions';
    ...
    // before
    const mapDispatchToProps = dispatch => {
         return {
             onIncrementCounter: () => dispatch({type: actionTypes.INCREMENT});
         }
    }
    // after
    const mapDispatchToProps = dispatch => {
         return {
             onIncrementCounter: () => dispatch(increment());
         }
    }
    

    Handling Asynchronous code

    take advantage of action creators to handle asynchronous code. to do this, we need redux-thunk.

    redux-thunk adds a middleware to the project which allows your action creators to be precise to not reurn the action itself but return a function which will eventually dispatch an action. With this little trick, not returning the action itself but a function which will then dispatch one, we can run asynchronous code because the eventually dispatched one part is the part which may run asynchronously.

    npm install --save redux-thunk
    
    // index.js
    import thunk from 'redux-thunk';
    ...
    // applyMiddleware allows us to add our own middleware to the store.
    const store =
    createStore(rootReducer, applyMiddleware(logger,thunk));
    
    
    //actions.js
    export const INCREMENT='INCREMENT';
    export const DECREMENT='DECREMENT';
    ...
    // before
    export const increment =()=>{
         return {
           type:INCREMENT
        }
    };
    // after 
    export const saveIncrement = () => {
       return {
           type:INCREMENT
        }
    }
    export const increment =()=>{
      return function (dispatch,getState){
    // we get dispatch here due to redux-thunk. middleware runs between 
    //the dispatching of an action and the point of time the ation reaches the 
    //reducer. the thing we do here is we still dispatch an action
    // but then redux-thunk comes in , steps in, has access to the action there. 
    //basiclly blocks the old action and dispatches it again in the future. 
    // now the new action will reach the reducer but in-between, 
    //redux-thunk is able to wait because it can dispatch an action 
    //whenever it wants. This is the asyncronous part and that is 
    //exactly allowing us to execute some asynchronous code inside.
           return dispatch => {
                setTimeout(() =>{
                       const oldCounter = getState().counter;
                       dispatch(saveIncrement());
                },2000);
           }
        }
        
    };
    
    image.png

    why redux saga?

    使用redux-thunk的一个结果是,会看到在 action creators 里, 处理异步的代码和dispatch action 的代码混合在一起。有时候我们会希望 action creators 或者说整个dispatch action 的过程 to be very clean. 我们不想在 action creators 里看到很多和dispatching action 不相关的代码。 这就是使用 redux-saga 的原因。

    redux saga is a package which follows a different approach of working with asynchronous code and it doesn't mix it with the act of dispatching actions.

    npm install --save redux-saga
    

    sagas are essentially kind of functions which you run up on certain actions and which handle all your side effect logic and a side effect simply is something like accessing local storage, reaching out to a server .

    新建文件夹 saga, 新建一个auth.js

    // put in the end will just dispatch a new action.
    import { put } from 'redux-saga/effects';
    import * as actionTypes from  '../actions/actionTypes';
    
    export function* logoutSaga (action) {
       yield localStorage.removeItem('token');
       put({
          type: actionTypes.AUTH_LOGOUT.
       });
    }
    

    hook it up to the store.

    // index.js
    ...
    import createSagaMiddleware from 'redux-saga';
    import { logoutSaga } from './store/sagas/auth';
    
    const sagaMiddleware = createSagaMiddleware();
    
    const store = createStore(rootReducer, 
    composeEnhancers(applyMiddleware(thunk, sagaMiddleware)
    ));
    sagaMiddleware.run(logoutSaga);
    
    

    moving logic from the action creator to Saga.

    //auth.js  
    ...
    export const logout = () =>{
        /* handle these side effects with redux saga. */
        //localStorage.removeItem('token');
        //localStorage.removeItem('expirationDate');
        //localStorage.removeItem('userId');
    
        return {
            type:actionTypes.AUTH_LOGOUT
        }
    }
    ...
    
    // actionType.js
    ...
    export const AUTH_INITIATE_LOGOUT = 'AUTH_INITIATE_LOGOUT';
    ...
    
    //auth.js  
    ...
    export const logout = () =>{
        /* handle these side effects with redux saga. */
        //localStorage.removeItem('token');
        //localStorage.removeItem('expirationDate');
        //localStorage.removeItem('userId');
    
        // whenever I dispatch logout, I will now
        // simply initiate the logout instead.
        // now the goal is ,to listen to this newly created 
       // action creator and execute our logout 
       // saga generator whenever we detect the initiate 
        logout call. 
       // for that,I'll create a new file in sagas folder.
        return {
            type:actionTypes.AUTH_INITIATE_LOGOUT
        }
    }
    ...
    

    在sagas文件夹中,新建index.js

    // index.js
    
    // takeEvery will allow us to listen to certain actions
    // and do something when they occur.
    import { takeEvery } from 'redux-saga/effects';
    import * as actionTypes from '../actions/actionTypes';
    import { logoutSaga } from './auth';
    
    export function* watchAuth(){
       yield takeEvery(actionTypes.AUTH_INITIATE_LOGOUT,lououtSaga);
    }
    
    // 全局 index.js 文件
    // before 
    import { logoutSaga } from './store/sagas/auth';
    // after
    import  { watchAuth } from './store/sagas';
    
    // before
    sagaMiddleware.run(logoutSaga);
    // after
    sagaMiddleware.run(watchAuth);
    
    

    相关文章

      网友评论

          本文标题:redux 综合

          本文链接:https://www.haomeiwen.com/subject/gfuqfqtx.html