这里以 App组件向Title组件经过传递为例react
connect的做用是对子组件进行一个包装, 减小每一个子组件都要写 contextTypes, 减小代码重复。数组
prop-types
这个插件是用来从父组件向子组件传递数据的 包含一个方法, 两个属性bash
父组件使用:闭包
static childContextTypes={
数据名: propTypes.数据类型
}
getChildContext() {
return {
数据名: 数据值
}
}
复制代码
子组件使用:app
static contextTypes = {
数据名: propTypes.数据类型
}
复制代码
下面是app.jsdom
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import * as serviceWorker from './serviceWorker';
import Title from './Tittle';
//父组件向子组件传递数据用的组件
import propTypes from "prop-types";
//createStore 函数
var createStore = function (appReducer) {
var state = null
var listeners = [];
var dispatch = function (action) {
state = appReducer(state, action);
listeners.forEach(e => e());
}
dispatch({})
var subscribe = function (listener) {
listeners.push(listener)
}
var getState = function () {
return state;
}
return {
dispatch,
subscribe,
getState
}
}
// reducer 定义store里面数据用的参数函数
let appReducer = (state, action) => {
if (!state) {
return {
title: "这个是标题",
}
}
switch (action.type) {
case "CHANGE_TITLE":
return {
...state,
title: action.newTitle
}
default:
return state;
}
}
//设置store
var store = createStore(appReducer)
// app组件
class App extends Component {
// 数据容器 父组件用的: childContextTypes
// 子组件用的 : contextTypes
// 在contextTypes, childContextTypes 中定义数据类型
// -->>>> console.log(Title.contextTypes.store == App.childContextTypes.store)
// 返回值是true
//--->>> childContextTypes 是用来传递的对象, 里面的store 和Title 组件contextTypes的store 都是一个
static childContextTypes = {
store: propTypes.object,
}
// 用来赋值要传递的数据
getChildContext() {
return {
store: store
}
}
constructor() {
super();
//订阅触发渲染的事件 this.setState
store.subscribe(() => {
this.setState(store.getState)
})
}
_chengeTitle(newTitle) {
store.dispatch({
type: "CHANGE_TITLE",
newTitle: newTitle
})
}
render() {
const { title, content } = store.getState();
console.info(store.getState())
return (
< div >
<Title store={store}></Title>
<button onClick={() => this._chengeTitle('您点击了按钮,标题已经被修改')}>点击</button>
</div >
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();
复制代码
下面是 Title.jside
import React, { Component } from 'react'
import propTypes from 'prop-types'
//---->>>> 简易版的connect
//用来作子组件包装的函数, 他负责以context方式收取, 再以props方式发送给子组件
let Connect = (WrapComponent) => {
return (
class Connect extends Component {
// 以context形式收取父组件发过来的信息
static contextTypes = {
store: propTypes.object
}
render() {
const { store } = this.context;
return (
//把store以props形式发给子组件
<WrapComponent store={store}>
</WrapComponent>
)
}
}
)
}
// 真正的子组件
class Title extends Component {
_changeTitle(newTitle) {
// 以props形式收取connect组件件发过来的信息
const { store } = this.props;
store.dispatch({
type: "CHANGE_TITLE",
title: newTitle
})
}
render() {
// 以props形式收取connect组件件发过来的信息
const { store } = this.props;
const { title } = store.getState();
return (
<h3 onClick={() => this._changeTitle('新的标题')}>{title} 点一下换个title</h3>
)
}
}
// 输出包装过的Title组件
export default Connect(Title)
复制代码
如今这个组件Connect 已经实现了store的传递,可是是整个store的传递,接下来实现,经过闭包的形式实现选择性的传递函数
import React, { Component } from 'react';
import propTypes from 'prop-types';
let Connect = (mapStateToProps, mapDispatchToProps) => {
return (WrapComponent) => {
return (
class Connect extends Component {
static contextTypes = {
store: propTypes.object
}
constructor() {
super();
//设置一个组件本身的state, 稍后加入子组件选择好的props传给子组件
this.state = {
allProps: {}
}
}
componentWillMount() {
//渲染前执行, 并触发更新事件
const { store } = this.context;
//渲染前进行第一次的初始化执行,保证被他包裹的组件能够收到props
this.updataProps();
//将更新props也存到store 的listeners事件数组中
// 在setState更新以后会, 也会更新本身的props, 并从新渲染本身的组件,保证被他包裹的组件能够收到更新事后的props
store.subscribe(() => this.updataProps())
}
updataProps() {
//updateProps在执行后是更新以后的组件渲染
let { store } = this.context
//选择哪些state经过props传递出去
// mapDispatchToProps(store.getState(), this.props) 传入全部的store里面的state 和在父级传过来的props。
// 他要return所选择的 store和props 里面的内容
let needStates = mapStateToProps ? mapStateToProps(store.getState(), this.props) : {};
// mapDispatchToProps(store.dispatch, this.state) 和在父级传过来的props。
// 他要return所选择的 store和props 里面的内容
let needDispatch = mapDispatchToProps ? mapDispatchToProps(store.dispatch, this.props) : {};
//结构
this.setState({
allProps: {
...needStates,
...needDispatch,
...this.props
}
})
}
render() {
return (
<WrapComponent {...this.state.allProps}>
</WrapComponent>
)
}
}
)
}
}
export default Connect;
复制代码
使用方法ui
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import * as serviceWorker from './serviceWorker';
import Connect from './connect'
//父组件向子组件传递数据用的组件
import propTypes from "prop-types";
//引入createStore
import createStore from './createStore'
// reducer 定义store里面数据用的参数函数
let appReducer = (state, action) => {
if (!state) {
return {
title: "这个是标题",
}
}
switch (action.type) {
case "CHANGE_TITLE":
return {
...state,
title: action.newTitle
}
default:
return state;
}
}
//设置store
var store = createStore(appReducer)
class Title extends Component {
render() {
console.log(this.props)
const { title, changeTitle } = this.props;
return (
<h3 onClick={() => changeTitle('新的标题')}>{title} 点一下换个title</h3>
)
}
}
// 这两个函数都要有返回值, props 目前仍是空对象,这取决于用户时候在真正的父组件是哟个props的方法传递到Connect组件
function mapStateToProps(state, props) {
console.log(state)
return {
title: state.title
}
}
function mapDispatchToProps(dispatch, props) {
return {
changeTitle: (newTitle) => {
dispatch({
type: "CHANGE_TITLE",
newTitle: newTitle
})
}
}
}
///// --->>>> connect的使用方法
export default Connect(mapStateToProps, mapDispatchToProps)(Title)
// app组件
class App extends Component {
static childContextTypes = {
store: propTypes.object,
}
getChildContext() {
return {
store: store
}
}
constructor() {
super();
//订阅触发渲染的事件 this.setState
store.subscribe(() => {
this.setState(store.getState)
})
}
_chengeTitle(newTitle) {
store.dispatch({
type: "CHANGE_TITLE",
newTitle: newTitle
})
}
render() {
const { title, content } = store.getState();
console.info(store.getState())
return (
< div >
<Title store={store}></Title>
<button onClick={() => this._chengeTitle('您点击了按钮,标题已经被修改')}>点击</button>
</div >
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();
复制代码
Provider组件的源码this
class Provider extends Component {
//用于检测本身子组件和props
static propTypes = {
store: propTypes.object,
children: propTypes.any
}
//发送store给子组件们
static childContextTypes = {
store: propTypes.object,
}
//
getChildContext() {
return {
store: store
}
}
//渲染本身和子组件
render() {
return (
<div>{this.props.children}</div>
)
}
}
复制代码
class App extends Component {
render() {
const { chengeTitle } = this.props;
console.log(chengeTitle)
return (
< div >
<h1>{title}</h1>
<button onClick={() => chengeTitle('您点击了按钮,标题已经被修改')}>点击</button>
</div >
)
}
}
function mapStateToProps(state, props) {
return {
state: state.title
}
}
function mapDispatchToProps(dispatch, props) {
return {
chengeTitle: (newTitle) => {
dispatch({
type: "CHANGE_TITLE",
newTitle: newTitle
})
}
}
}
App = Connect(mapStateToProps, mapDispatchToProps)(App)
//使用Provider 组件
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root'));
serviceWorker.unregister();
复制代码