您好,欢迎来到伴沃教育。
搜索
您的当前位置:首页redux 综合

redux 综合

来源:伴沃教育

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);

Copyright © 2019- bangwoyixia.com 版权所有 湘ICP备2023022004号-2

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务