Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

从底层原理学习 react-redux #79

Open
george-es opened this issue Jan 8, 2021 · 0 comments
Open

从底层原理学习 react-redux #79

george-es opened this issue Jan 8, 2021 · 0 comments
Labels

Comments

@george-es
Copy link
Owner

react-redux

在 react 中,一个状态可能被多个组件依赖或者影响,在此,没有一套应用状态管理方案,只能够通过状态提升实现,层级少还没问题,层级多,那就会变的非常复杂。

后来,学习了 context,我们知道,可以把共享状态放到父组件的 context 上,这个父组件下所有的组件都可以从 context 中直接获取到状态而不需要一层层地进行传递了。但是直接从 context 里面存放、获取数据增强了组件的耦合性;并且所有组件都可以修改 context 里面的状态就像谁都可以修改共享状态一样,导致程序运行的不可预料。

redux 和 react-redux 有什么区别?

Redux 和 React-redux 并不是同一个东西。React-redux 就是把 Redux 这种架构模式和 React.js 结合起来的一个库,就是 Redux 架构在 React.js 中的体现。

react-redux 原理

react-redux 实际上就是 context 和 redux 的组合,通过高阶函数模式封装了 connect 函数Provider 容器组件,context 的特点是可以组件透传,redux 的特点是单向数据流,修改数据只能用 dispatch。

说干就干,我们来实现一个 connect

connect 设计思想

  • 是一个高阶组件

  • 将 store 的 state 和 dispatch 注入到 mapStateToProps 和 mapDispatchToProps 中

  • 返回一个 props 里面有处理过的 state 和 dispach

  • 有个 mapStateToProps 参数告诉 store 需要哪些数据(不可能把 state 的全部取出来吧)读数据

    我们设计一下 mapStateToProps

    • 它是一个函数
    • 返回需要在 store 中提取的 state 对象
    const mapStateToProps = (state) => {
    	return {
    		a: state.a,
    		b: state.b,
    		...
    	}
    }
    
  • 有个 mapDispatchToProps 参数用于触发 store 的 dispatch,也就是写数据

    我们设计一下 mapDispatchToProps

    • 它是个函数
    • 返回一个需要在 store 中调用 dispatch 的对象
    const mapDispatchToProps = (dispatch) => {
    	return {
    		a: (value) => dispatch({ type: 'UPDATE', value })
    		...
    	}
    }
    
import { MyContext } from './provider'
export connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
	class Connect extends Component {
		constructor() {
			super()
			this.state = {
				allProps: {}
			}
		}
		
		componentWillMount() {
			this._updateProps()
			this.context.store.subscribe(() => this._updateProps())
		}
		
		_updateProps() {
			let statePorps = mapStateToProps
				? mapStateToProps(this.context.store.getState())
        		: {} // 防止 mapStateToProps 没有传入
        	let dispatchProps = mapDispatchToProps
                ? mapDispatchToProps(this.context.store.dispatch, this.props)
                : {} // 防止 mapDispatchToProps 没有传入
            this.setState({
                allProps: {
                  ...stateProps,
                  ...dispatchProps
                }
            })
		}
		render() {
			return <WrappedComponent {...this.state.allProps}/>
		}
	}
	Connect.contextType = MyContext
	return Connect
}

探究完 connect 我们再看看 Provider 是怎么实现的

Provider 原理

要用 context 就是因为要把 store 存放到里面,好让子组件 connect 的时候能够取到 store。我们可以额外构建一个组件来做这种事情,然后让这个组件成为组件树的根节点,那么它的子组件都可以获取到 context 了。

我们把这个组件叫 Provider,它提供(provide)了 store

export const MyContext = React.createContext({}) 
export class Provider extends Component {
	render() {
		return (
			<MyContext.Provider value={this.props.store}>
				{this.props.children}
			</MyContext.Provider>
		)
	}
}

使用

./store.js

...  // redecers 代码就不写了,一个 action 一个 state 组合 reducers,这里生成 store
export default createStore(reducers)

./index.js


import React from 'react'
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'
import store from 'store'

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}> // 使用 Provider 在最顶层注入
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

./children.js

import React from 'react'
import { connect } from 'react-redux'
class Child extends React.Component {
    render() {
        console.log(this.props.data, '我拿到 store 里面数据了')
        return (
            <div>
               Hello world
            </div>

        )
    }
}
const mapStateToProps = (state) => {
    return {
        data: state
    }
}
export default connect(mapStateToProps)(Chlid)
@george-es george-es added the react label Jan 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant