redux笔记

Middleware

作用就相当于后端的过滤器,可以被链式组合,它提供的是位于action被发起后,到达reducer之前的扩展点,可以进行日志记录,创建崩溃报告,调用异步接口或路由等。

模拟几个中间件

柯里化:

const logger = store => next => action => {
	console.log('dispatching', action);
	let result = next(action);
	console.log('next state', store.getState());
	return result;
}
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
}

上述代码是箭头函数写法等同于

	function logger(store) {
		return function wrapDispatchToAddLogging(next) {
			return function dispatchAndLog(action) {
				console.log('dispatching', action);
				let result = next(action);
				console.log('next state', store.getState());
				return result;
			}
		}
	}

把各个中间件捏在一起:这也是定义中间件时为什么要柯里化的原因:store一直是一样的,而dispatch需要使用上次包装后的新的dispatch,action则是在最终业务代码中传入的。

function applyMiddleware(store, middlewares) {
	middlewares = middlewares.slice();
	middlewares.reverse();
	let dispatch = store.dispatch;
	middlewares.forEach(middleware => (dispatch = middleware(store)(dispatch)));
	return Object.assign({}, store, {dispatch})
}

// 在每一个 middleware 中变换 dispatch 方法。
	middlewares.forEach(middleware => (store.dispatch = middleware(store)))

要将用dispatch参数保存下当前的store.dispatch,在每一个 middleware 中变换 dispatch 方法。而不是好像是为了 =====>>>>
它用了一个非常巧妙的方式,以确保如果你在 middleware 中调用的是 store.dispatch(action) 而不是 next(action),那么这个操作会再次遍历包含当前 middleware 在内的整个 middleware 链。这对异步的 middleware 非常有用,正如我们在之前的章节中提到的。在创建阶段调用 dispatch 时你需要特别注意,详见下方警告。
暂时看不太懂。。。

实际使用:

import { createStore, combineReducers, applyMiddleware } from 'redux';

const todoApp = combineReducers(reducers);
const store = createStore(
	todoApp,
	applyMiddleware(logger, crashReporter)
)
//疑问: 实际使用中,并没有传入store, 直接传入的就是各个中间件,那上述我们自己写的applyMiddleware方法中的第一个参数是哪里来的。	

结合Router

在使用router时候仍然使用Provider包裹传入store

import { Provider } from 'react-redux';
...

const Root = ({store}) => (
	<Provider store={store}>
		<Router>
			<Route path="/:id" component={App} />
		</Router>
	</Provider>
);
...
const store = createStore(todoApp);
render(<Root store={store} />, document.getElementById('root'));

mapStateToProps方法的第一个参数state就是store, 第二个参数ownProps是其父组件传递给他的props;

拓展Redux功能

使用middleware (拓展dispatch函数的功能) 或 enhancer(拓展store的功能) 来拓展Redux store的功能。
封装为configureStore.js :

import { applyMiddleware, compose, createStore } from 'redux';
import thunkMiddleware from 'redux-thunk';

import monitorReducersEnhancer from './enhancers/monitorReducers';
import loggerMiddleware from './middleware/logger';
import rootReducer from './reducers';

export default function configureStore(preloadedState) {
	const middlewares = [loggerMiddleware, thunkMiddleware];
	const middlewareEnhancer = applyMiddleware(...middlewares);
	
	const enhancers = [middlewareEnhancer, monitorReducerEnhancer];
	const composedEnhancers = compose(...enhancers);
	
	const store = createStore(rootReducer, preloadedState, composeEnhancers);

	return store;
}

计算衍生数据

Reselect库可以创建可记忆的、可组合的selector函数。Reselect selectors可以用来高效的计算Redux store里的衍生数据。(比如,TodoList筛选have done的item)
Reselect 提供 createSelector 函数来创建可记忆的 selector。createSelector 接收一个 input-selectors 数组和一个转换函数作为参数。如果 state tree 的改变会引起 input-selector 值变化,那么 selector 会调用转换函数,传入 input-selectors 作为参数,并返回结果。如果 input-selectors 的值和前一次的一样,它将会直接返回前一次计算的数据,而不会再调用一次转换函数。

import { createSelector } from 'reselect;
const getVisibilityFilter = (state, props) => state.todoLists[props.listId].visibilityFilter;
const getTodos = state => state.todos;
export const getVisibleTodos = createSelector(
	[getVisibilityFilter, getTodos],  //由方法组成的数组
	(visibilityFilter, todos) => {
		switch(visibilityFilter) {
			case 'SHOW_ALL':
				return todos
			case 'SHOW_DONE':
				return todos.filter(t => t.completed)
			//...
		}
	}
)

使用:
const mapStateToProps = (state, props) => {
	return {
		todos: getVisibleTodos(state, props)
	}
}

可以组合: 把getVisibleTodos当作input-selectors的其中一个。
问题: 上述如果交换渲染listId=1和listId=2就会导致每次传入的input-selectors不一样,因此会重新计算而不是取缓存,这样会降低性能。解决方案: 每一个组件实例都有一个自己的selector。原理:如果 connect 的 mapStateToProps 返回的不是一个对象而是一个函数,他将被用做为每个容器的实例创建一个单独的 mapStateToProps 函数。

const makeGetVisibleTodos = () => {
    return createSelector(
        [getVisibilityFilter, getTodos],
        (visibilityFilter, todos) => {
            switch (visibilityFilter) {
                case 'SHOW_DONE': 
                    return todos.filter(todo => todo.completed)
                //...
            }
        }
    )
}

const makeMapStateToProps = () => {
    const getVisibleTodos = makeGetVisibleTodos();
    const mapStateToProps = (state, props) => {
        return {
            todos: getVisibleTodos(state, props)
        }
    }
    return mapStateToProps
} 

const VisibleTodoList = connect(
    makeMapStateToProps,
    mapDispatchToProps
)(TodoList)

撤销重做

其实就是多定义past和future数组,类似浏览器history前进或回退一样进出栈。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值