重点在源码部分,用法能够翻阅文档或者googlereact
// redux 状态管理 把数据集中存放
// 当前全部组件的状态
function createStore(reducer) {
let state;
let listeners = []
let getState = ()=> state; // 把对象克隆一份
let dispatch = (action)=>{
state = reducer(state,action);
listeners.forEach(fn=>fn());
}
let subscribe = (fn)=>{
listeners.push(fn);
return ()=>{
listeners = listeners.filter(l=>fn !== l);
}
}
dispatch({type:'@INIT'})
return {
subscribe,
getState,
dispatch
}
}
// 1.redux 中不能直接操做状态
// 2.若是任意一个组件中想更新状态,须要派发一个动做
// 3.每次更新状态 最好用一个新的状态对像覆盖掉 (时间旅行)
let initState = { // 状态已经抽离出去了不能直接更改
title: { content: '你好', color: 'red' },
content: { content: '哈哈', color: 'green' }
}
function reducer(state = initState,action){ // reducer的参数 有两个第一个就是用户派发的动做,第二个就是当前组件的状态
switch (action.type) {
case 'CHANGE_TITLE_COLOR':
return {...state,title:{...state.title,color:action.color}}
case 'CHANGE_CONTENT_CONTENT':
return {...state,content:{...state.content,content:action.content}}
}
return state
}
let store = createStore(reducer);
store.subscribe(renderApp); //dispatch的时候 就会触发renderApp
let unsub = store.subscribe(()=>console.log('呵呵 更新了'))
setTimeout(() => {
// 实现状态更新的方法
store.dispatch({type:'CHANGE_TITLE_COLOR',color:'pink'});
}, 1000);
setTimeout(() => {
unsub();
store.dispatch({ type: 'CHANGE_CONTENT_CONTENT', content: '新的内容' });
}, 2000);
function renderTitle(){
let title = document.getElementById('title');
title.innerHTML = store.getState().title.content;
title.style.background = store.getState().title.color;
}
function renderContent() {
let content = document.getElementById('content');
content.innerHTML = store.getState().content.content;
content.style.background = store.getState().content.color;
}
function renderApp(){
renderTitle();
renderContent();
}
renderApp();
复制代码
看完上面的简易redux模拟实现 回顾一下几个重点内容redux
getState = () => { //这样以后咱们经过getState获取的实际就是state的克隆,直接改就不会影响到state
// return state redux源码实际上是这样的 并无像下面这样包一层,因此咱们看到能够改变数据可是会有报错提示
return JSON.parse(JSON.stringfy(state))
}
复制代码
dispatch({
type: '',
payload: ''
})
复制代码
在没有reducer以前 咱们要想更改状态都是在createStore里面去改,为了解藕,须要将业务逻辑抽离出来app
function createStore(state, reducer) {
function getState() {
},
function dispatch(action) {
//dispatch的结果是告诉reducer要改什么
state = reducer(state, action)
}
return {
dispatch,
getState
}
}
function reducer(state, action) {
}
复制代码
如何解决每次更改状态都要从新执行renderApp()的问题 --- 》 发布订阅异步
在redux源码中解读订阅事件async
function dispatch(action) {
<!--if (!isPlainObject(action)) {-->
<!-- throw new Error(--> <!-- 'Actions must be plain objects. ' +--> <!-- 'Use custom middleware for async actions.'--> <!-- )--> <!--}--> <!--if (typeof action.type === 'undefined') {--> <!-- throw new Error(--> <!-- 'Actions may not have an undefined "type" property. ' +--> <!-- 'Have you misspelled a constant?'--> <!-- )--> <!--}--> <!--if (isDispatching) {--> <!-- throw new Error('Reducers may not dispatch actions.')--> <!--}--> try { isDispatching = true <!--currentReducer就是当前正在执行的reducer--> currentState = currentReducer(currentState, action) } finally { isDispatching = false } const listeners = (currentListeners = nextListeners) for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() //执行全部的订阅事件 每每是 this.setState({}) react的setState会比较先后的两个值 若是值变化了才会从新渲染 因此不用担忧 因此组件都订阅后会所有this.setState致使不须要的视图刷新 } return action } <!--咱们看到 每一个组件要是想用redux都须要订阅 这个很麻烦 react-redux就是为了解决这个问题的--> 复制代码
咱们在学习一个新东西的时候始终要想那么几个问题:1. 怎么用?2. 为何要用他或者说他解决了什么问题?ide
// store全局注入
import {Provider} from 'react-redux'
ReactDOM.render(<Provider store={store}> <> <Counter></Counter> <Todo></Todo> <Test><p>xxxx</p><p>xxxx</p></Test> </> </Provider>,window.root);
复制代码
// actions/connter.js
import * as Types from '../action-types';
// action
export default {
add(val){
return {type:Types.INCREMENT,count:val}
}
}
复制代码
//组件内部使用
import React from 'react';
import actions from '../store/actions/counter';
import { connect } from 'react-redux';
class Counter extends React.Component {
handleClick = () => {
this.props.add(3); // connect传过来的
}
render() {
return (
<> <p>{this.props.number}</p> <button onClick={this.handleClick}>点击增长</button> </> ) } } export default connect( (state) => ({ ...state.counter }), //返回的均可以经过this.props.xxx获取到 actions )(Counter); 复制代码
为了解决每次都须要在组件中将属性绑定到状态,还要订阅数据的问题 咱们来看看react-redux是怎么解决的函数
react中的provider学习
// context.js
import React from 'react';
let Context = React.createContext();
export default Context;
复制代码
//provider源码
import React,{Component} from 'react';
import Context from './context';
// Provider 主要是提供store使用的
export default class Provider extends Component{
render(){
return (<Context.Provider value={{ store: this.props.store}}> {this.props.children} </Context.Provider>) } } 复制代码
//connect源码
import React from 'react';
import Context from './context';
import { bindActionCreators} from 'redux';
let connect = (mapStateToProps, mapDipsatchToProps)=> (Component)=>{
return ()=>{
class Proxy extends React.Component{
state = mapStateToProps(this.props.store.getState()); //将redux中是数据放到state身上,
//当redux中的数据变化时-- 》 此state变化 --》 此组件刷新 --》 儿子组件(Component)刷新
//这样就作到了 当redux数据变化时,组件会自动刷新而不须要再每一个组件中订阅了
componentDidMount(){
this.unsub = this.props.store.subscribe(()=>{
this.setState(mapStateToProps(this.props.store.getState()))
})
}
componentWillUnmount(){
this.unsub();
}
render(){
let mapDispatch; //将dispatch传到做为store的action参数 dispatch
/**{ add: function() { return dispatch(add.apply(this, arguments)) }, minis: function() { return dispatch(minis.apply(this, arguments)) } }*/
if (typeof mapDipsatchToProps === 'object'){ //若是第二个参数传递的是一个对象,把对象直接进行包装便可
mapDispatch = bindActionCreators(mapDipsatchToProps,this.props.store.dispatch);
}else{
mapDispatch = mapDipsatchToProps(this.props.store.dispatch);
}
return <Component {...this.state} {...mapDispatch}></Component>
}
}
return <Context.Consumer> {({store})=>{ // 将状态 和dispatch 拿到执行函数 把结果对象传递给本来的component渲染 return <Proxy store={store}></Proxy> }} </Context.Consumer> } } export default connect; 复制代码
以上就是我我的对redux的学习和理解,分享给你们,固然还有不少不足之处但愿你们指出,这里reducer都是纯函数,没法处理异步问题,那在业务代码中确定是少不了异步数据的请求的,怎么解决呢?下一节开始学习redux-saga,看看是怎么解决异步问题的!ui