±à¼ÍƼö: |
ÎÄÕ·ÖÎöÁËreact-reduxµÄ¸ÅÄÂß¼¸´ÓõÄÏêϸ½éÉÜ£¬ÒÔ¼°Ð¾ÉÁ½¸ö°æ±¾µÄ
context API£¬Ï£Íû¿ÉÒÔΪÄúµÄѧϰ´øÀ´°ïÖú¡£
±¾ÎÄÀ´×ÔÓÚ¾ò½ð¼¼ÊõרÀ¸£¬ÓÉ»ðÁú¹ûÈí¼þLuca±à¼¡¢ÍƼö¡£ |
|
react-redux ÊÇʲô
react-redux ÊÇ redux ¹Ù·½ React °ó¶¨¿â¡£Ëü°ïÖúÎÒÃÇÁ¬½ÓUI²ãºÍÊý¾Ý²ã¡£±¾ÎÄÄ¿µÄ²»ÊǽéÉÜ
react-redux µÄʹÓ㬶øÊÇÒª¶¯ÊÖʵÏÖÒ»¸ö¼òÒ×µÄ react-redux£¬Ï£ÍûÄܹ»¶ÔÄãÓÐËù°ïÖú¡£
Ê×ÏÈ˼¿¼Ò»Ï£¬ÌÈÈô²»Ê¹Óà react-redux£¬ÎÒÃÇµÄ react ÏîÄ¿ÖиÃÈçºÎ½áºÏ redux
½øÐпª·¢ÄØ¡£
ÿ¸öÐèÒªÓë redux ½áºÏʹÓõÄ×é¼þ£¬ÎÒÃǶ¼ÐèÒª×öÒÔϼ¸¼þÊ£ºÔÚ×é¼þÖлñÈ¡ store ÖеÄ״̬
¼àÌý store ÖÐ״̬µÄ¸Ä±ä£¬ÔÚ״̬¸Ä±äʱ£¬Ë¢ÐÂ×é¼þ
ÔÚ×é¼þÐ¶ÔØÊ±£¬ÒƳý¶Ô״̬±ä»¯µÄ¼àÌý¡£
ÈçÏÂ:
import React
from 'react';
import store from '../store';
import actions from '../store/actions/counter';
/**
* reducer ÊÇ combineReducer({counter, ...})
* state µÄ½á¹¹Îª
* {
* counter: {number: 0},
* ....
* }
*/
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
number: store.getState().counter.number
}
}
componentDidMount() {
this.unsub = store.subscribe(() => {
if(this.state.number === store.getState().counter.number)
{
return;
}
this.setState({
number: store.getState().counter.number
});
});
}
render() {
return ( <div> <p>{`number:
${this.state.number}`}</p> <button
onClick={() => {store.dispatch(actions.add(2))}}>+</button>
<button onClick={() => {store.dispatch(actions.minus(2))}}>-</button>
<div>
)
}
componentWillUnmount() {
this.unsub();
}
} |
½¨Á¢¿í´øÁ¬½ÓÈç¹ûÎÒÃǵÄÏîÄ¿ÖÐÓкܶà×é¼þÐèÒªÓë redux ½áºÏʹÓã¬ÄÇôÕâЩ×é¼þ¶¼ÐèÒªÖØ¸´Ð´ÕâЩÂß¼¡£ÏÔÈ»£¬ÎÒÃÇÐèÒªÏë°ì·¨¸´ÓÃÕⲿ·ÖµÄÂß¼£¬²»È»»áÏÔµÃÎÒÃǺܴÀ¡£ÎÒÃÇÖªµÀ£¬react
Öи߽××é¼þ¿ÉÒÔʵÏÖÂß¼µÄ¸´Óá£
ÎÄÖÐËùÓõ½µÄ [Counter ´úÂë] (github.com/YvetteLau/B¡) ÖÐµÄ myreact-redux/counter
ÖУ¬½¨ÒéÏÈ clone ´úÂ룬µ±È»À²£¬Èç¹û¾õµÃ±¾ÎIJ»´íµÄ»°£¬¸ø¸östar¹ÄÀø¡£
Âß¼¸´ÓÃ
ÔÚ src Ŀ¼ÏÂн¨Ò»¸ö react-redux Îļþ¼Ð£¬ºóÐøµÄÎļþ¶¼Ð½¨ÔÚ´ËÎļþ¼ÐÖС£
´´½¨ connect.js Îļþ
Îļþ´´½¨ÔÚ react-redux/components Îļþ¼ÐÏÂ:
ÎÒÃǽ«Öظ´µÄÂß¼±àд connect ÖС£
import React,
{ Component } from 'react';
import store from '../../store';
export default function connect (WrappedComponent)
{
return class Connect extends Component {
constructor(props) {
super(props);
this.state = store.getState();
}
componentDidMount() {
this.unsub = store.subscribe(() => {
this.setState({
this.setState(store.getState());
});
});
}
componentWillUnmount() {
this.unsub();
}
render() {
return ( <WrappedComponent {...this.state}
{...this.props}/>
)
}
}
} |
½¨Á¢¿í´øÁ¬½ÓÓиöССµÄÎÊÌ⣬¾¡¹ÜÕâÂß¼ÊÇÖØ¸´µÄ£¬µ«ÊÇÿ¸ö×é¼þÐèÒªµÄÊý¾ÝÊDz»Ò»ÑùµÄ£¬²»Ó¦¸Ã°ÑËùÓеÄ״̬¶¼´«µÝ¸ø×é¼þ£¬Òò´ËÎÒÃÇÏ£ÍûÔÚµ÷ÓÃ
connect ʱ£¬Äܹ»½«ÐèÒªµÄ״̬ÄÚÈݸæÖª connect¡£ÁíÍ⣬×é¼þÖпÉÄÜ»¹ÐèÒªÐÞ¸Ä״̬£¬ÄÇôҲҪ¸æËß
connect£¬ËüÐèÒªÅÉ·¢ÄÄЩ¶¯×÷£¬·ñÔò connect ÎÞ·¨ÖªµÀ¸Ã°ó¶¨ÄÇЩ¶¯×÷¸øÄã¡£
Ϊ´Ë£¬ÎÒÃÇÐÂÔöÁ½¸ö²ÎÊý£ºmapStateToProps ºÍ mapDispatchToProps£¬ÕâÁ½¸ö²ÎÊý¸ºÔð¸æËß
connect ×é¼þÐèÒªµÄ state ÄÚÈݺͽ«ÒªÅÉ·¢µÄ¶¯×÷¡£
mapStateToProps ºÍ mapDispatchToProps
ÎÒÃÇÖªµÀ mapStateToProps ºÍ mapDispatchToProps
µÄ×÷ÓÃÊÇʲô£¬µ«ÊÇĿǰΪֹ£¬ÎÒÃÇ»¹²»Çå³þ£¬ÕâÁ½¸ö²ÎÊýÓ¦¸ÃÊÇÒ»¸öʲôÑùµÄ¸ñʽ´«µÝ¸ø connect ȥʹÓá£
import { connect
} from 'react-redux';
....
//connect µÄʹÓÃ
export default connect(mapStateToProps, mapDispatchToProps)(Counter); |
mapStateToProps ¸æËß connect £¬×é¼þÐèÒª°ó¶¨µÄ״̬¡£
mapStateToProps ÐèÒª´ÓÕû¸ö״̬ÖÐÌôÑ¡×é¼þÐèÒªµÄ״̬£¬µ«ÊÇÔÚµ÷Óà connect ʱ£¬ÎÒÃDz¢²»ÄÜ»ñÈ¡µ½
store £¬²»¹ý connect ÄÚ²¿ÊÇ¿ÉÒÔ»ñÈ¡µ½ store µÄ£¬Îª´Ë£¬ÎÒÃǽ« mapStateToProps
¶¨ÒåΪһ¸öº¯Êý£¬ÔÚ connect ÄÚ²¿µ÷ÓÃËü£¬½« store ÖÐµÄ state ´«µÝ¸øËü£¬È»ºó½«º¯Êý·µ»ØµÄ½á¹û×÷ΪÊôÐÔ´«µÝ¸ø×é¼þ¡£×é¼þÖÐͨ¹ý
this.props.XXX À´»ñÈ¡¡£Òò´Ë£¬mapStateToProps µÄ¸ñʽӦ¸ÃÀàËÆÏÂÃæÕâÑù:
//½« store.getState()
´«µÝ¸ø mapStateToProps
mapStateToProps = state => ({
number: state.counter.number
}); |
mapDispatchToProps ¸æËß connect£¬×é¼þÐèÒª°ó¶¨µÄ¶¯×÷¡£
»ØÏëһϣ¬×é¼þÖÐÅÉ·¢¶¯×÷£ºstore.dispatch({actions.add(2)})¡£connect
°ü×°Ö®ºó£¬ÎÒÃÇÈÔÒªÄÜÅÉ·¢¶¯×÷£¬¿Ï¶¨ÊÇ this.props.XXX() ÕâÑùµÄÒ»ÖÖ¸ñʽ¡£
±ÈÈ磬¼ÆÊýÆ÷µÄÔö¼Ó£¬µ÷Óà this.props.add(2)£¬¾ÍÊÇÐèÒªÅÉ·¢ store.dispatch({actions.add(2)})£¬Òò´Ë
add ÊôÐÔ£¬¶ÔÓ¦µÄÄÚÈݾÍÊÇ (num) => { store.dispatch({actions.add(num)})
}¡£´«µÝ¸ø×é¼þµÄÊôÐÔÀàËÆÏÂÃæÕâÑù:
{
add: (num) => {
store.dispatch(actions.add(num))
},
minus: (num) => {
store.dispatch(actions.minus(num))
}
} |
ºÍ mapStateToProps Ò»Ñù£¬ÔÚµ÷Óà connect ʱ£¬ÎÒÃDz¢²»ÄÜ»ñÈ¡µ½
store.dispatch£¬Òò´ËÎÒÃÇÒ²ÐèÒª½« mapDispatchToProps Éè¼ÆÎªÒ»¸öº¯Êý£¬ÔÚ
connect ÄÚ²¿µ÷Óã¬ÕâÑù¿ÉÒÔ½« store.dispatch ´«µÝ¸øËü¡£ËùÒÔ£¬mapStateToProps
Ó¦¸ÃÊÇÏÂÃæÕâÑùµÄ¸ñʽ:
//½« store.dispacth
´«µÝ¸ø mapDispatchToProps
mapDispatchToProps = (dispatch) => ({
add: (num) => {
dispatch(actions.add(num))
},
minus: (num) => {
dispatch(actions.minus(num))
}
}) |
ÖÁ´Ë£¬ÎÒÃÇÒѾ¸ãÇå³þ mapStateToProps ºÍ mapDispatchToProps µÄ¸ñʽ£¬ÊÇʱºò½øÒ»²½¸Ä½ø
connect ÁË¡£
connect 1.0 °æ±¾
import React,
{ Component } from 'react';
import store from '../../store';
export default function connect (mapStateToProps,
mapDispatchToProps) {
return function wrapWithConnect (WrappedComponent)
{
return class Connect extends Component {
constructor(props) {
super(props);
this.state = mapStateToProps(store.getState());
this.mappedDispatch = mapDispatchToProps(store.dispatch);
}
componentDidMount() {
this.unsub = store.subscribe(() => {
const mappedState = mapStateToProps(store.getState());
//TODO ×öÒ»²ãdz±È½Ï£¬Èç¹û״̬ûÓиı䣬Ôò²»setState
this.setState(mappedState);
});
}
componentWillUnmount() {
this.unsub();
}
render() {
return ( <WrappedComponent {...this.props}
{...this.state} {...this.mappedDispatch} />
)
}
}
}
} |
ÎÒÃÇÖªµÀ£¬connect ÊÇ×÷Ϊ react-redux ¿âµÄ·½·¨ÌṩµÄ£¬Òò´ËÎÒÃDz»¿ÉÄÜÖ±½ÓÔÚ
connect.js ÖÐÈ¥µ¼Èë store£¬Õâ¸ö store Ó¦¸ÃÓÉʹÓà react-redux µÄÓ¦Óô«Èë¡£react
ÖÐÊý¾Ý´«µÝÓÐÁ½ÖÖ£ºÍ¨¹ýÊôÐÔ props »òÕßÊÇͨ¹ýÉÏÏÂÎĶÔÏó context£¬Í¨¹ý connect
°ü×°µÄ×é¼þÔÚÓ¦ÓÃÖзֲ¼£¬¶ø context Éè¼ÆÄ¿µÄÊÇΪÁ˹²ÏíÄÇЩ¶ÔÓÚÒ»¸ö×é¼þÊ÷¶øÑÔÊÇ¡°È«¾Ö¡±µÄÊý¾Ý¡£
ÎÒÃÇÐèÒª°Ñ store ·ÅÔÚ context ÉÏ£¬ÕâÑù¸ù×é¼þϵÄËùÓÐ×ÓËï×é¼þ¶¼¿ÉÒÔ»ñÈ¡µ½ store¡£Õⲿ·ÖÄÚÈÝ£¬ÎÒÃǵ±È»¿ÉÒÔ×Ô¼ºÔÚÓ¦ÓÃÖбàдÏàÓ¦´úÂ룬²»¹ýºÜÏÔÈ»£¬ÕâЩ´úÂëÔÚÿ¸öÓ¦ÓÃÖж¼ÊÇÖØ¸´µÄ¡£Òò´ËÎÒÃǰÑÕⲿ·ÖÄÚÈÝÒ²·â×°ÔÚ
react-redux ÄÚ²¿¡£
´Ë´¦£¬ÎÒÃÇʹÓÃ¾ÉµÄ Context API À´Ð´(¼øÓÚÎÒÃÇʵÏÖµÄ react-redux 4.x ·ÖÖ§µÄ´úÂ룬Òò´ËÎÒÃÇʹÓþɰæµÄ
context API)¡£
Provider
ÎÒÃÇÐèÒªÌṩһ¸ö Provider ×é¼þ£¬ËüµÄ¹¦ÄܾÍÊǽÓÊÕÓ¦Óô«µÝ¹ýÀ´µÄ store£¬½«Æä¹ÒÔÚ context
ÉÏ£¬ÕâÑùËüµÄ×ÓËï×é¼þ¾Í¶¼¿ÉÒÔͨ¹ýÉÏÏÂÎĶÔÏó»ñÈ¡µ½ store¡£
н¨ Provider.js Îļþ
Îļþ´´½¨ÔÚ react-redux/components Îļþ¼ÐÏ£º
import React,
{ Component } from 'react';
import PropTypes from 'prop-types';
export default class Provider extends Component
{
static childContextTypes = {
store: PropTypes.shape({
subscribe: PropTypes.func.isRequired,
dispatch: PropTypes.func.isRequired,
getState: PropTypes.func.isRequired
}).isRequired
}
constructor(props) {
super(props);
this.store = props.store;
}
getChildContext() {
return {
store: this.store
}
}
render() {
/**
* Ôçǰ·µ»ØµÄÊÇ return Children.only(this.props.children)
* µ¼ÖÂProviderÖ»Äܰü¹üÒ»¸ö×Ó×é¼þ£¬ºóÀ´È¡ÏûÁË´ËÏÞÖÆ
* Òò´Ë´Ë´¦£¬ÎÒÃÇÖ±½Ó·µ»Ø this.props.children
*/
return this.props.children
}
} |
н¨Ò»¸ö index.js Îļþ
Îļþ´´½¨ÔÚ react-redux Ŀ¼ÏÂ:
´ËÎļþÖ»×öÒ»¼þÊ£¬¼´½« connect ºÍ Provider µ¼³ö
import connect
from './components/connect';
import Provider from './components/Provider';
export {
connect,
Provider
} |
Provider µÄʹÓÃ
ʹÓÃʱ£¬ÎÒÃÇÖ»ÐèÒªÒýÈë Provider£¬½« store ´«µÝ¸ø
Provider¡£
mport React,
{ Component } from 'react';
import { Provider } from '../react-redux';
import store from './store';
import Counter from './Counter';
export default class App extends Component {
render() {
return ( <Provider store={store}>
<Counter /> </Provider>
)
}
} |
ÖÁ´Ë£¬Provider µÄÔ´ÂëºÍʹÓÃÒѾ˵Ã÷Çå³þÁË£¬²»¹ýÏàÓ¦µÄ connect
Ò²ÐèÒª×öһЩÐ޸ģ¬ÎªÁËͨÓÃÐÔ£¬ÎÒÃÇÐèÒª´Ó context ÉÏÈ¥»ñÈ¡ store£¬È¡´ú֮ǰµÄµ¼Èë¡£
connect 2.0 °æ±¾
import React,
{ Component } from 'react';
import PropTypes from 'prop-types';
export default function connect(mapStateToProps,
mapDispatchToProps) {
return function wrapWithConnect(WrappedComponent)
{
return class Connect extends Component {
//PropTypes.shape Õⲿ·Ö´úÂëÓë Provider ÖÐÖØ¸´£¬Òò´ËºóÃæÎÒÃÇ¿ÉÒÔÌáÈ¡³öÀ´
static contextTypes = {
store: PropTypes.shape({
subscribe: PropTypes.func.isRequired,
dispatch: PropTypes.func.isRequired,
getState: PropTypes.func.isRequired
}).isRequired
}
constructor(props, context) {
super(props, context);
this.store = context.store;
//Ô´ÂëÖÐÊǽ« store.getState() ¸øÁË this.state
this.state = mapStateToProps(this.store.getState());
this.mappedDispatch = mapDispatchToProps(this.store.dispatch);
}
componentDidMount() {
this.unsub = this.store.subscribe(() => {
const mappedState = mapStateToProps(this.store.getState());
//TODO ×öÒ»²ãdz±È½Ï£¬Èç¹û״̬ûÓиı䣬ÔòÎÞÐè setState
this.setState(mappedState);
});
}
componentWillUnmount() {
this.unsub();
}
render() {
return ( <WrappedComponent {...this.props}
{...this.state} {...this.mappedDispatch} />
)
}
}
}
} |
ʹÓà connect ¹ØÁª Counter Óë store ÖеÄÊý¾Ý¡£
import React,
{ Component } from 'react';
import { connect } from '../react-redux';
import actions from '../store/actions/counter';
class Counter extends Component {
render() {
return ( <div> <p>{`number:
${this.props.number}`}</p> <button
onClick={() => { this.props.add(2) }}>+</button>
<button onClick={() => { this.props.minus(2)
}}>-</button> </div>
)
}
}
const mapStateToProps = state => ({
number: state.counter.number
});
const mapDispatchToProps = (dispatch) => ({
add: (num) => {
dispatch(actions.add(num))
},
minus: (num) => {
dispatch(actions.minus(num))
}
});export default connect(mapStateToProps, mapDispatchToProps)(Counter); |
store/actions/counter.js ¶¨ÒåÈçÏ£º
import { INCREMENT,
DECREMENT } from '../action-types';
const counter = {
add(number) {
return {
type: INCREMENT,
number
}
},
minus(number) {
return {
type: DECREMENT,
number
}
}
}
export default counter; |
ÖÁ´Ë£¬ÎÒÃÇµÄ react-redux ¿âÒѾ¿ÉÒÔʹÓÃÁË£¬²»¹ýºÜÓкܶàϸ½ÚÎÊÌâ´ý´¦Àí:mapDispatchToProps
µÄ¶¨ÒåдÆðÀ´ÓеãÂé·³£¬²»¹»¼ò½à
´ó¼ÒÊÇ·ñ»¹¼ÇµÃ redux ÖÐµÄ bindActionCreators£¬½èÖúÓÚ´Ë·½·¨£¬ÎÒÃÇ¿ÉÒÔÔÊÐí´«µÝ
actionCreator ¸ø connect£¬È»ºóÔÚ connect ÄÚ²¿½øÐÐת»»¡£connect
ºÍ Provider ÖÐµÄ store µÄ PropType ¹æÔò¿ÉÒÔÌáÈ¡³öÀ´£¬±ÜÃâ´úÂëµÄÈßÓàmapStateToProps
ºÍ mapDispatchToProps ¿ÉÒÔÌṩĬÈÏÖµ
mapStateToProps ĬÈÏֵΪ state => ({}); ²»¹ØÁª state£»
mapDispatchToProps µÄĬÈÏֵΪ dispatch
=> ({dispatch})£¬½« store.dispatch ·½·¨×÷ΪÊôÐÔ´«µÝ¸ø±»°ü×°µÄÊôÐÔ¡£Ä¿Ç°£¬ÎÒÃǽö´«µÝÁË
store.getState() ¸ø mapStateToProps£¬µ«ÊǺܿÉÄÜÔÚɸѡ¹ýÂËÐèÒªµÄ state
ʱ£¬ÐèÒªÒÀ¾Ý×é¼þ×ÔÉíµÄÊôÐÔ½øÐд¦Àí£¬Òò´Ë£¬¿ÉÒÔ½«×é¼þ×ÔÉíµÄÊôÐÔÒ²´«µÝ¸ø mapStateToProps£¬Í¬ÑùµÄÔÒò£¬Ò²½«×ÔÉíÊôÐÔ´«µÝ¸ø
mapDispatchToProps¡£
connect 3.0 °æ±¾
ÎÒÃǽ« store µÄ PropType ¹æÔòÌáÈ¡³öÀ´£¬·ÅÔÚ utils/storeShape.js
ÎļþÖС£
dz±È½ÏµÄ´úÂë·ÅÔÚ utils/shallowEqual.js ÎļþÖУ¬Í¨ÓõÄdz±È½Ïº¯Êý£¬´Ë´¦²»Áгö£¬ÓÐÐËȤ¿ÉÒÔÖ±½ÓÔĶÁÏ´úÂë¡£
import React,
{ Component } from 'react';
import { bindActionCreators } from 'redux';
import storeShape from '../utils/storeShape';
import shallowEqual from '../utils/shallowEqual';
/**
* mapStateToProps ĬÈϲ»¹ØÁªstate
* mapDispatchToProps ĬÈÏֵΪ dispatch => ({dispatch})£¬½«
`store.dispatch` ·½·¨×÷ΪÊôÐÔ´«µÝ¸ø×é¼þ
*/
const defaultMapStateToProps = state => ({});
const defaultMapDispatchToProps = dispatch =>
({ dispatch });
export default function connect(mapStateToProps,
mapDispatchToProps) {
if(!mapStateToProps) {
mapStateToProps = defaultMapStateToProps;
}
if (!mapDispatchToProps) {
//µ± mapDispatchToProps Ϊ null/undefined/false...ʱ£¬Ê¹ÓÃĬÈÏÖµ
mapDispatchToProps = defaultMapDispatchToProps;
}
return function wrapWithConnect(WrappedComponent)
{
return class Connect extends Component {
static contextTypes = {
store: storeShape
};
constructor(props, context) {
super(props, context);
this.store = context.store;
//Ô´ÂëÖÐÊǽ« store.getState() ¸øÁË this.state
this.state = mapStateToProps(this.store.getState(),
this.props);
if (typeof mapDispatchToProps === 'function')
{
this.mappedDispatch = mapDispatchToProps(this.store.dispatch,
this.props);
} else {
//´«µÝÁËÒ»¸ö actionCreator ¶ÔÏó¹ýÀ´
this.mappedDispatch = bindActionCreators(mapDispatchToProps,
this.store.dispatch);
}
}
componentDidMount() {
this.unsub = this.store.subscribe(() => {
const mappedState = mapStateToProps(this.store.getState(),
this.props);
if (shallowEqual(this.state, mappedState)) {
return;
}
this.setState(mappedState);
});
}
componentWillUnmount() {
this.unsub();
}
render() {
return ( <WrappedComponent {...this.props}
{...this.state} {...this.mappedDispatch} />
)
}
}
}
} |
ÏÖÔÚ£¬ÎÒÃÇµÄ connect ÔÊÐí mapDispatchToProps
ÊÇÒ»¸öº¯Êý»òÕßÊÇ actionCreators ¶ÔÏó£¬ÔÚ mapStateToProps ºÍ mapDispatchToProps
ȱʡ»òÕßÊÇ null ʱ£¬Ò²ÄܱíÏÖÁ¼ºÃ¡£
²»¹ý»¹ÓÐÒ»¸öÎÊÌ⣬connect ·µ»ØµÄËùÓÐ×é¼þÃû¶¼ÊÇ Connect£¬²»±ãÓÚµ÷ÊÔ¡£Òò´ËÎÒÃÇ¿ÉÒÔΪÆäÐÂÔö
displayName¡£
connect 4.0 °æ±¾
import React,
{ Component } from 'react';
import { bindActionCreators } from 'redux';
import storeShape from '../utils/storeShape';
import shallowEqual from '../utils/shallowEqual';
/**
* mapStateToProps ȱʡʱ£¬²»¹ØÁªstate
* mapDispatchToProps ȱʡʱ£¬ÉèÖÃÆäĬÈÏֵΪ dispatch =>
({dispatch})£¬½«`store.dispatch` ·½·¨×÷ΪÊôÐÔ´«µÝ¸ø×é¼þ
*/
const defaultMapStateToProps = state => ({});
const defaultMapDispatchToProps = dispatch =>
({ dispatch });
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name
|| 'Component';
}
export default function connect(mapStateToProps,
mapDispatchToProps) {
if(!mapStateToProps) {
mapStateToProps = defaultMapStateToProps;
}
if(!mapDispatchToProps) {
//µ± mapDispatchToProps Ϊ null/undefined/false...ʱ£¬Ê¹ÓÃĬÈÏÖµ
mapDispatchToProps = defaultMapDispatchToProps;
}
return function wrapWithConnect (WrappedComponent)
{
return class Connect extends Component {
static contextTypes = storeShape;
static displayName = `Connect(${getDisplayName(WrappedComponent)})`;
constructor(props) {
super(props);
//Ô´ÂëÖÐÊǽ« store.getState() ¸øÁË this.state
this.state = mapStateToProps(store.getState(),
this.props);
if(typeof mapDispatchToProps === 'function') {
this.mappedDispatch = mapDispatchToProps(store.dispatch,
this.props);
}else{
//´«µÝÁËÒ»¸ö actionCreator ¶ÔÏó¹ýÀ´
this.mappedDispatch = bindActionCreators(mapDispatchToProps,
store.dispatch);
}
}
componentDidMount() {
this.unsub = store.subscribe(() => {
const mappedState = mapStateToProps(store.getState(),
this.props);
if(shallowEqual(this.state, mappedState)) {
return;
}
this.setState(mappedState);
});
}
componentWillUnmount() {
this.unsub();
}
render() {
return ( <WrappedComponent {...this.props}
{...this.state} {...this.mappedDispatch} />
)
}
}
}
} |
ÖÁ´Ë£¬react-redux ÎÒÃǾͻù±¾ÊµÏÖÁË£¬²»¹ýÕâ¸ö´úÂë²¢²»ÍêÉÆ£¬±ÈÈ磬ref
¶ªÊ§µÄÎÊÌ⣬×é¼þµÄ props ±ä»¯Ê±£¬ÖØÐ¼ÆËã this.state ºÍ this.mappedDispatch£¬Ã»ÓнøÒ»²½½øÐÐÐÔÄÜÓÅ»¯µÈ¡£Äã¿ÉÒÔÔÚ´Ë»ù´¡ÉϽøÒ»²½½øÐд¦Àí¡£
react-redux Ö÷¸É·ÖÖ§µÄ´úÂëÒѾʹÓà hooks ¸Äд£¬ºóÆÚÈç¹ûÓÐʱ¼ä£¬»áÊä³öһƪа汾µÄ´úÂë½âÎö¡£
×îºó£¬Ê¹ÓÃÎÒÃÇ×Ô¼º±àдµÄ react-redux ºÍ redux ±àдÁË Todo µÄdemo£¬¹¦ÄÜÕý³££¬´úÂëÔÚ
ÔÚ https://github.com/YvetteLau/Blog ÖÐµÄ myreact-redux/todo
Ï¡£
¸½ÉÏÐÂÀÏ context API µÄʹÓ÷½·¨£º
context
ĿǰÓÐÁ½¸ö°æ±¾µÄ context API£¬¾ÉµÄ API ½«»áÔÚËùÓÐ 16.x °æ±¾Öеõ½Ö§³Ö£¬µ«ÊÇδÀ´°æ±¾ÖÐ»á±»ÒÆ³ý¡£
context API(ÐÂ)
const MyContext
= React.createContext(defaultValue); |
´´½¨Ò»¸ö Context ¶ÔÏó¡£µ± React äÖȾһ¸ö¶©ÔÄÁËÕâ¸ö
Context ¶ÔÏóµÄ×é¼þ£¬Õâ¸ö×é¼þ»á´Ó×é¼þÊ÷ÖÐÀë×ÔÉí×î½üµÄÄǸöÆ¥ÅäµÄ Provider ÖжÁÈ¡µ½µ±Ç°µÄ
context Öµ¡£
×¢Ò⣺ֻÓе±×é¼þËù´¦µÄÊ÷ÖÐûÓÐÆ¥Åäµ½ Provider ʱ£¬Æä defaultValue ²ÎÊý²Å»áÉúЧ¡£
ʹÓÃ
Context.js
Ê×ÏÈ´´½¨ Context ¶ÔÏó
import React
from 'react';
const MyContext = React.createContext(null);
export default MyContext; |
¸ù×é¼þ( Pannel.js )
½«ÐèÒª¹²ÏíµÄÄÚÈÝ£¬ÉèÖÃÔÚ <MyContext.Provider> µÄ value ÖÐ(¼´
context Öµ)
×Ó×é¼þ±» <MyContext.Provider> °ü¹ü
import React
from 'react';
import MyContext from './Context';
import Content from './Content';
class Pannel extends React.Component {
state = {
theme: {
color: 'rgb(0, 51, 254)'
}
}
render() {
return (
// ÊôÐÔÃû±ØÐë½Ð value <MyContext.Provider value={this.state.theme}>
<Content /> </MyContext.Provider>
)
}
} |
×ÓËï×é¼þ( Content.js )
Àà×é¼þ
¶¨Òå Class.contextType: static contextType
= ThemeContext;
ͨ¹ý this.context »ñÈ¡ <ThemeContext.Provider> ÖÐ
value µÄÄÚÈÝ(¼´ context Öµ)
//Àà×é¼þ
import React from 'react';
import ThemeContext from './Context';
class Content extends React.Component {
//¶¨ÒåÁË contextType Ö®ºó£¬¾Í¿ÉÒÔͨ¹ý this.context »ñÈ¡ ThemeContext.Provider
value ÖеÄÄÚÈÝ
static contextType = ThemeContext;
render() {
return ( <div style={{color: `2px solid
${this.context.color}`}}>
//.... </div>
)
}
} |
º¯Êý×é¼þ
×ÓÔªËØ°ü¹üÔÚ <ThemeContext.Consumer>
ÖÐ
<ThemeContext.Consumer> µÄ×ÓÔªËØÊÇÒ»¸öº¯Êý£¬Èë²Î
context Öµ£¨Provider ÌṩµÄ value£©¡£´Ë´¦ÊÇ {color: XXX}
import React
from 'react';
import ThemeContext from './Context';
export default function Content() {
return ( <ThemeContext.Consumer>
{
context => ( <div style={{color: `2px
solid ${context.color}`}}>
//.... </div>
)
} </ThemeContext.Consumer>
)
} |
context API(¾É)
ʹÓÃ
¶¨Òå¸ù×é¼þµÄ childContextTypes (ÑéÖ¤ getChildContext ·µ»ØµÄÀàÐÍ)
¶¨Òå getChildContext ·½·¨
¸ù×é¼þ( Pannel.js )
import React
from 'react';
import PropTypes from 'prop-types';
import Content from './Content';
class Pannel extends React.Component {
static childContextTypes = {
theme: PropTypes.object
}
getChildContext() {
return { theme: this.state.theme }
}
state = {
theme: {
color: 'rgb(0, 51, 254)'
}
}
render() {
return (
// ÊôÐÔÃû±ØÐë½Ð value <> <Content />
</>
)
}
} |
×ÓËï×é¼þ( Content.js )
¶¨Òå×ÓËï×é¼þµÄ contextTypes (ÉùÃ÷ºÍÑéÖ¤ÐèÒª»ñÈ¡µÄ״̬µÄÀàÐÍ)
ͨ¹ý this.context ¼´¿ÉÒÔ»ñÈ¡´«µÝ¹ýÀ´µÄÉÏÏÂÎÄÄÚÈÝ¡£
import React
from 'react';
import PropTypes from 'prop-types';
class Content extends React.Component {
static contextTypes = PropTypes.object;
render() {
return ( <div style={{color: `2px solid
${this.context.color}`}}>
//.... </div>
)
}
} |
²Î¿¼´úÂë |