這裏以 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();
複製代碼