<Provider>
ă¨connect
ă§ĺ˛ăžăăăăŽăĺç
§ă§ăăŞăăăă§ă
// test
let component = shallow(<Provider store={store}><ContainerComponent /></Provider>);
component.find('#abc'); // returns null
let component = shallow(<Provider store={store}><div id="abc"></div></Provider>);
component.find('#abc'); // returns the div node
// ContainerComponent
const Component = ({...}) => (<div id="abc"></div>);
export default connect(..., ...)(Component);
https://github.com/reactjs/redux/issues/1481ă§ăăšăăé ľç´ ă§č¨čż°ăăăäžăăăŠăăžăăăăăłăłăăăźăŻăăăă§ăăšăăăăăă¨ăŻăăăžăăă ă ăăç§ăŻăšăăźăăłăłăăăăăšăăăšă/ă§ăăăăŠăăăăăăžăăăďź
@fshowalterä˝ăă˘ă¤ăă˘ăŻăăăžăăďź
connect()
ăProvider
ăăăŽăŠă¤ăăŠăŞăŽä¸é¨ă§ăŻăăăžăăă 䝣ăăăŤăéŠĺăŞăŞăă¸ăăŞďź https://github.com/reactjs/react-reduxďźăŤăăĄă¤ăŤăăăŚăăĺ ´ĺăŻăăăŽăăăŞĺéĄăŤă¤ăăŚčŠąăĺăă追补ăăćšăç°Ąĺă§ăă
ăăăŻĺŽéăŤăŻçăŤăăŞăŁăŚăăă¨ćăăžăă ćľ ăăŹăłăăŞăłă°ăčĄăŁăŚăăăăăăăăă¤ăăźăłăłăăźăăłăăŽăżăăŹăłăăŞăłă°ăăăžăăă¤ăžăăContainerComponentăŻăăŞăă¸ă§ăŻăĺşĺăăŠăźă ăžăăŻćľ ăăŹăłăăŞăłă°ă§çćăăăăăŽăŤăăŽăžăžćŽăăăžăă
ăăă§2ă¤ăŽčăďź
ăžăă connect()
ăŤăăŁăŚçćăăăăŠăăăźăłăłăăźăăłăăŻăĺŽéăŤăŻcontext.store
ăć¤ç´˘ăăĺăŤprops.store
ăć¤ç´˘ăăăăăĺŽéăŤćĽçśăăăăłăłăăźăăłăăăăšăăăĺż
čŚăăăă¨ćăăĺ ´ĺăŻă ăăăăă¤ăăźăăłăłăăăšăăŞăŠăć°ăŤăăăă¨ăŞăă <ConnectedComponent store={myTestStore} />
ăăŹăłăăŞăłă°ă§ăăăŻăă§ăă
2çŞçŽăŽčłŞĺăŻăĺŽĺ
¨ăŤćĽçśăăăăłăłăăźăăłăăĺŽéăŤăăšăăăăă¨ăŤă¤ăăŚćŹĺ˝ăŤĺżé
ăăĺż
čŚăăăăăŠăăă§ăă ç§ăčŚăč°čŤăŻăçšĺŽăŽĺ°éĺ
ˇă使ăŁăŚăăăŹăźăłăăłăłăăźăăłăăăăšăă§ăă mapStateToProps
ĺŽčŁ
ăăăšăă§ăăă°ăreact-reduxăăăăăćŁăăăžă¨ăăă¨ĺŽĺ
¨ăŤćłĺŽă§ăăă¨ăăăăŽă§ăăćĽçśăăăăăźă¸ă§ăłčŞä˝ăĺŽéăŤăăšăăăĺż
čŚăŻăăăžăăă
@gaearonăăă§ăăăăăăăŞăăă ăăăreactă§ä¸ăăăé ľç´ ăŹăă§ä¸ăăăăăăăŞăăŁăă
@markeriksonăšăăźăăłăłăăźăăłăăăăšăăăççąăŻăăŠăăăăăăłăłăăźăăłăăŤăăŁăŚéŠĺăŞăăŁăšăăăăŁăźăĺźăłĺşăăăăă¨ă確čŞăăăăŁămapToDispatchProps
ăŽăăă§ăă ăšăă˘ăConnectedComponent
ăŤć¸Ąăă ăă§ăçść
ăăăăłă°ăăăŁăšăăăăłăźăŤăăăŻé˘ć°ăăăšăăăžăăă
ăăăŤă¤ăăŚăăŁă¨čăăĺż čŚăŤĺżăăŚé˘éŁăăăŹăă§ĺéĄăć辡ăăžăă ĺŠăăŚăăăŚăăăă¨ăă
@mordra ďźăćŁăăăăŁăšăăăăŁăźăĺźă°ăăăă¨č¨ăă¨ăăĺŽéăŤăŻăćŁăăă˘ăŻăˇă§ăłăŻăŞă¨ăźăżăźăăćĺłăăžăăďź
ăăŞăăŻăăŽăăăŞăă¨ăăăăă¨ăă§ăăžăďźć§ćăŻăăăăăŞăă§ăăăăăŞăăŻă˘ă¤ăă˘ăĺžăĺż čŚăăăăžăďźďź
let actionSpy = sinon.spy();
let wrapper = shallow(<PlainComponent someActionProp={actionSpy} />);
wrapper.find(".triggerActionButton").simulate("click");
expect(actionSpy.calledOnce).to.be.true;
expect(actionSpy.calledWith({type : ACTION_I_WAS_EXPECTING)).to.be.true;
ă¤ăžăăăăŹăźăłăłăłăăźăăłăăăŹăłăăŞăłă°ăă mapDispatch
ăăčżăăăă˘ăŻăˇă§ăłă§ăăĺ°éĺ
ˇăŽăšăă¤ă渥ăăăłăłăăźăăłăăăăăăŽă˘ăŻăˇă§ăłăĺźăłĺşăăăăŤĺż
čŚăŞĺä˝ăăăŞăŹăźăăžăă
ăžăăç§ăŽäťĽĺăŽăłăĄăłăăŤăăă¨ă mapStateToProps
ă¨mapDispatchToProps
ĺĽă
ăŤăăšăă§ăăćĽçśăăăăăźă¸ă§ăłăăăšăăăăă¨ăŞăăReact-ReduxăăăăăéŠĺăŤĺźăłĺşăăă¨ăă§ăăăŻăă§ăăăăăäşĺŽă§ăăăă¨ă確čŞăăăăăŤăăčŞä˝ă
@markeriksonç§ăŽPlainComponent
ăŻă˘ăŻăˇă§ăłăă˘ăŻăˇă§ăłăŽä˝ćč
ăŤă¤ăăŚçĽăăŞăăăăăăŞăăććĄăăŚăăăă¨ăŻă§ăăžăăă ăăăćŁăăćšćłăăŠăăăŻăăăăžăăăă揥ăŽăă¨ăčŚăŚăă ăăă
https://github.com/mordra/cotwmtor/blob/master/client/charCreation/charCreation.jsx
ç§ăŽmapDispatchToProps
ăŤăŻăăšăŚăŽăă¸ăăŻăĺŤăžăăŚăăăă¨ăăăăăžăă
const mapDispatchToProps = (dispatch) => {
return {
onCompleted : (player) => {
Meteor.call('newGame', player, function (data) {
console.log('new game return: ' + data);
});
dispatch(routeActions.push('/game'));
dispatch({type: "INIT_GAME", map: generateAreas(), buildings: generateBuildings(dispatch)});
},
onCancelled : () => dispatch(routeActions.push('/')),
onChangeName : input => dispatch(actions.changeName(input)),
onChangeGender : gender => dispatch(actions.setGender(gender)),
onSetDifficulty : lvl => dispatch(actions.setDifficulty(lvl)),
onChangeAttribute: (attr, val) => dispatch(actions.setAttribute(attr, val))
}
};
ăăăăŁăŚăPlainComponentăŤć¸Ąăăăĺ°éĺ
ˇăăšăă¤ăăĺ ´ĺăćŁăăă˘ăŻăˇă§ăłăĺźăłĺşăăăŚăăăăŠăăăăăšăăăăă¨ăŻă§ăăžăăăăăăăă˘ăŻăˇă§ăłä˝ćč
ăŤć¸ĄăăăăăŠăĄăźăżăźă ăăćŁăăă§ăă
揥ăŤă connect
ăłăłăăźăăłăăĺĺĽăŤăăšăăăĺż
čŚăăăăžăă
ăăă ăăăŻç§ăăăŁă¨ç解ăăăŽăŤĺ˝šçŤăĄăžăă
ă§ăăăăç§ăĺŽéăŤăăšăăć¸ăĺŽéăŽçľé¨ăŻăťă¨ăăŠăŞăă¨ăăčŚĺă¨ă¨ăăŤăăăă¤ăăŽčăăăăăžăă
dispatch
ăŤă˘ăŻăťăšăăăăŽă§ăă˘ăŻăˇă§ăłăŻăŞă¨ăźăżăźăŻĺĺĽăŤĺŽçžŠăăăŚăăžăăă ăăčŞä˝ăŻç°ĄĺăŤăăšăă§ăăžăăă ăăăăŽĺä˝ăăăšăă§ăăăăăŤăăăĺ ´ĺăăăăžăăăăŽăăăŤăŻă mapDispatch
ăŽĺ¤é¨ă§ăăăăçŹčŞăŽé˘ć°ă¨ăăŚĺŽçžŠăăĺż
čŚăăăăžăădispatch
ăŤă˘ăŻăťăšă§ăăăăăŤăŞăăžăăthis.props.onSetDifficulty("HARD")
ăŞăŠăĺŽčĄăăŚăăžăă ç§ăčĄĺăŽăăăŤăšăă¤ă渥ăăă¨ăććĄăăă¨ăăăăăŻç§ă罎ăćăăăă¨ăććĄăăăăăŞăăŽă§ăă ăăăăŁăŚăăšăă¤ăonSetDifficulty
ă§ć¸Ąăăĺ ´ĺăăłăłăăźăăłăăăăăĺźăłĺşăăăă¨ă確čŞăăéŁć庌ăŽč¨ąĺŽšĺ¤ă渥ăăĺŻč˝ć§ăăăăžăăćçľçăŤăŻă mapDispatch
ă§ĺŽçžŠăăăŚăăé˘ć°ă使ç¨ăăŚăăăăăĺĺĽăŤĺŽçžŠăăăă¨ă§ăçŠäşăăăăăšăăăăăăăăă¨ăă§ăăăŻăă§ăă ăăăăă°ăăłăłăăźăăłăăéŠĺăŤăăšăăăăăăŤmapDispatch
ăćĽçśăăĺż
čŚăăăăă¨ăĺżé
ăăĺż
čŚăăŞăăŞăăăłăłăăźăăłăăéŠĺăŞĺźć°ăŞăŠă使ç¨ăăŚçšĺŽăŽpropăłăźăŤăăăŻăĺźăłĺşăăăăŠăăăŤçŚçšăĺ˝ăŚăăă¨ăă§ăăžăă
ăžăăăłăłăăźăăłăăŽčŚłçšăăăŻăćçľçăŤăŻă {action: CHANGE_NAME, name : "Fred"}
ăăăŁăšăăăăăăăăŠăăăŤă¤ăăŚĺŽéăŤĺżé
ăăĺż
čŚăŻăăăžăăă ăăăçĽăŁăŚăăăŽăŻăăăăthis.props.onChangeName("Fred")
ă¨ĺźă°ăăŚăăăă¨ăăăăŚ_that_ăăăŞăăăăšăăăăă¨ăăŚăăăăŽă§ăăă¨ăăăă¨ă§ă-ăăăŻćŁăăćšćłă§propăłăźăŤăăăŻăĺźăłĺşăăžăăă
@mordraăŽĺ ´ĺă mapDispatchToProps
ăă¨ăŻăšăăźăăăŚăĺĺĽăŤăăšăăăžăă ăăăăăŁĺăăăšăŚćŁăăăă¨ă確čŞăăăšăă¤ă渥ăăŚă˘ăŻăˇă§ăłăŻăŞă¨ăźăżăźăăăšăăăăă¨ăă§ăăžăă
ă¨ăŻč¨ăăăŽăŽăç§ăŻmapDispatchToProps
ăéżăăŚăĺçŹă§ç°ĄĺăŤăăšăă§ăăăă§ăŤĺ˝˘ćăăăă˘ăŻăˇă§ăłăŻăŞă¨ăźăżăźăăăăăămapActionCreatorsToProps
ăĺŞĺ
ăăĺžĺăăăăžăă ăăŽĺ ´ĺăă¤ăłăăźăăŽăżă¤ăăăšăé˛ăăăăŤăăšăŚăŽĺ°éĺ
ˇăĺŽçžŠăăăŚăăăă¨ăăăšăăăžăă
ćĺžăŤăé常ăŽďźćľ ăăŞăďźăŹăłăăŞăłă°ă使ç¨ă§ăăăă¨ăŤćł¨ćăăŚăă ăăă ăăŽăăăŤăŻjsdomăžăăŻĺŽéăŽăăŠăŚăśăŽăăăăăĺż čŚă§ăăăăăŽĺžăŻäťťćăŽăŹăăŤă桹ăăŹăłăăŞăłă°ă§ăăžăă
ăăăreactăžăăŻé ľç´ ăŹăă§ä¸ăăăăŠăăăăăăŞăăŁă
çłă訳ăăăžăăăăReactăžăăŻEnzymeăŞăă¸ăăŞăćĺłăăăăŽă§ăŻăăăžăăă§ăăă ç§ăŻăăŞăă使ç¨ăăŚăăăŠă¤ăăŠăŞă§ăăReactReduxăćĺłăăžăăă
ĺŠăăŚăăăŚăăăă¨ăăăăăŻç§ăčĄăŁăŚćśĺăăăăăŽăăăăăŽć ĺ ąă§ăă @markerikson @fshowalterăăĺ°ă調ăšăŚămapState / Dispatchăă¨ăŻăšăăźăăăăăăăĺĺĽăŤăăšăăăăă¨ăććĄăăžăăćĺž ăăăă˘ăŻăˇă§ăłăĺźăłčľˇăăăłăźăŤăăăŻăăăšăăăăŽăŻçăŤăăŞăŁăŚăăžăă
@gaearonăšăăźăăŹăšăłăłăăźăăłăăŻăćľ ăăŚĺéĄăăăăĺăă ăă§ăŻăŹăłăăŞăłă°ăăăžăăăăšăăźăăŹăšăłăłăăźăăłăăăăšăăăăšăăźăăăŤăłăłăăźăăłăăŽăŹăłăăŞăłă°ăŤĺéĄăăăăăă§ăăăăŽăăăäťăŽă¨ăăăăăŽăăšăĺéżăăăăăŤĺżăŤçăăŚăăăžăă
ăăŞăăč¨ĺăăŚăăĺéĄăăăăăžăăă ćŠč˝ăłăłăăźăăłăă§ćľ ăăŹăłăăŞăłă°ăăăžă使ç¨ă§ăăžăă ăă ăăćľ ăăŹăłăăŞăłă°ăŻ1ăŹăăŤăŽćˇąăă§ăăćŠč˝ăăžăăďźăłăłăăźăăłăăăŠăŽăăăŤĺŽçžŠăăăŚăăŚăďźă ăăă丝ăŞćŠč˝ă§ăăĺ帰ăăŞăăăăăłăłăăźăăłăăŽăăšăăŻçŹçŤăăăžăžă§ăă ĺçžă§ăăćľ ăăŹăłăăŞăłă°ăŤçšĺŽăŽĺéĄăăăĺ ´ĺăŻăReactăŞăă¸ăăŞăŤĺŻžăăŚăă°ăĺ ąĺăăŚăă ăăă ăăăă¨ăďź
@gaearon ďźć確ăŤăăăăăŤăç§ăăăăčĄăĺ ´ĺďź
let wrapper = shallow(<A><B /></A>);
BăŻăŹăłăăŞăłă°ăăăžăăďź ăăăĺ
ăŽčłŞĺă ăŁăă¨ćăăŽă§ăćĽçśăăăšăăăăăăŤăćĽçśăăăăłăłăăźăăłăăŽĺ¨ăăŤ<Provider>
ăăŹăłăăŞăłă°ăăăă¨ăăžăăă
Reduxă¨ăŻç´ćĽé˘äżăăăžăăăă ăłăłăăă§shallow()
ăĺ庌ĺźăłĺşăăă¨ă§ăăăă解湺ăăžăăă ăšăă˘çść
ă渥ăăăĺ
é¨ăłăłăăźăăłăăăŹăłăăŞăłă°ăăžăă
äžďź
import React from 'react';
import {expect} from 'chai';
import {shallow} from 'enzyme';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import events from 'events';
const mockStore = configureMockStore([ thunk ]);
const storeStateMock = {
myReducer:{
someState: 'ABC'
}
};
let store;
let component;
describe('tests for MyContainerComponent', () => {
beforeEach(() => {
store = mockStore(storeStateMock);
component = shallow(<MyContainerComponent store={store} />).shallow();
});
it('renders container', () => {
expect(component.find('div.somediv')).to.have.length.of(1);
});
...
});
ă彚ăŤçŤăŚăă°
ăăăăăŤăšăżă ă˘ă¸ăĽăźăŤăäťăăŚăăăčĄăăăăŽăă茪ăăżăăăćšćłăä˝ćăăĺż čŚăăăăžăăďź
硨éďźăăăăŁăŚă component = shallowWithConnect()
ăŞăŠăŽEnzymeăŽ1ă¤ăŽăĄă˝ăăă¨ăăŚćĽçśă厣č¨ă§ăăžă
@ev1stensbergăăăŻç´ ć´ăăăă˘ă¤ăă˘ă§ăă ăăăă˘ăăăźăă§ăăăăŠăăăăăăł/ăžăăŻăăăŤé˘ăăä˝ćĽăéĺ§ăăăăăŠăăăĺ¤ćăăžăăăďź ăăă§ăŞăăă°ăç§ăŻč˛˘çŽăăăă¨ćăăžăă
ĺ°ćĽăăŽĺéĄăŤééăăäşşăŽăăăŤăăăăç§ăŽăăăŤĺăă˘ăăăźăă§ăďźăăčŞä˝ă§ăăŹăźăłăłăłăăźăăłăăăăšăăăă ăă§ăă ăłăłăăźăăłăĺŽçžŠăĺĺäťăă¨ăŻăšăăźăă¨ăăŚă¨ăŻăšăăźăăăćĽçśăăăăłăłăăźăăłăďźă˘ăăŞă§ä˝żç¨ăăăăďźăăăăŠăŤăăŽă¨ăŻăšăăźăă¨ăăŚă¨ăŻăšăăźăăăžăă ĺ ăŽčłŞĺăŽăłăźăăŤćťăăžăă
// test
let component = shallow(<Provider store={store}><ContainerComponent /></Provider>);
component.find('#abc'); // returns null
let component = shallow(<Provider store={store}><div id="abc"></div></Provider>);
component.find('#abc'); // returns the div node
// ContainerComponent
export const Component = ({...}) => (<div id="abc"></div>); // I EXPORT THIS, TOO, JUST FOR TESTING
export default connect(..., ...)(Component);
揥ăŤă
const component = shallow(<Component {...props} />)
// test component
ăžăăäťăŽäşşăä¸ă§ććăăăăăŤăćĽçśăŤć¸Ąăé˘ć°ăăŤăăźăăŚăăĺ ´ĺăŻăăłăłăăźăăłăĺ ¨ä˝ăĺŽĺ ¨ăŤăŤăăźăăŚăăĺż čŚăăăăžăă
ăăŚăăăă§ćĺăŤćł¨ćăăăă¨ăŻă
ç§ă大ä¸ĺ¤ŤăŞăăăăŞăăŻ2ă¤ăŽăă¨ăăăšăăăĺż čŚăăăă ăă§ăďź
ăăăç§ăŽă˘ăăăźăă§ă
import React from 'react';
import { shallow } from 'enzyme';
import { fromJS } from 'immutable';
import { createStore } from 'redux';
// this is the <Map /> container
import Map from '../index';
// this is an action handled by a reducer
import { mSetRegion } from '../reducer';
// this is an action handled by a saga
import { sNewChan } from '../sagas';
// this is the component wrapped by the <Map /> container
import MapComponent from '../../../components/Map';
const region = {
latitude: 20.1449858,
longitude: -16.8884463,
latitudeDelta: 0.0222,
longitudeDelta: 0.0321,
};
const otherRegion = {
latitude: 21.1449858,
longitude: -12.8884463,
latitudeDelta: 1.0222,
longitudeDelta: 2.0321,
};
const coordinate = {
latitude: 31.788,
longitude: -102.43,
};
const marker1 = {
coordinate,
};
const markers = [marker1];
const mapState = fromJS({
region,
markers,
});
const initialState = {
map: mapState,
};
// we are not testing the reducer, so it's ok to have a do-nothing reducer
const reducer = state => state;
// just a mock
const dispatch = jest.fn();
// this is how i mock the dispatch method
const store = {
...createStore(reducer, initialState),
dispatch,
};
const wrapper = shallow(
<Map store={store} />,
);
const component = wrapper.find(MapComponent);
describe('<Map />', () => {
it('should render', () => {
expect(wrapper).toBeTruthy();
});
it('should pass the region as prop', () => {
expect(component.props().region).toEqual(region);
});
it('should pass the markers as prop', () => {
expect(component.props().markers).toEqual(markers);
});
// a signal is an action wich will be handled by a saga
it('should dispatch an newChan signal', () => {
component.simulate('longPress', { nativeEvent: { coordinate } });
expect(dispatch.mock.calls.length).toBe(1);
expect(dispatch.mock.calls[0][0]).toEqual(sNewChan(coordinate));
});
// a message is an action wich will be handled by a reducer
it('should dispatch a setRegion message', () => {
component.simulate('regionChange', otherRegion);
expect(dispatch.mock.calls.length).toBe(2);
expect(dispatch.mock.calls[1][0]).toEqual(mSetRegion(otherRegion));
});
});
@markeriksonăŠăăăćĺłă§ăăďź
mapStateToPropsăŽĺŽčŁ ăăăšăă§ăăžă
ES6ă¨ăŻăšăăźăăäťăăŚćĽçśăăăăłăłăăăă¨ăŻăšăăźăăăă¨ăăžăă ăăŠă¤ăăźămapStateToPropsăŤă˘ăŻăťăšăăăă¨ăŻă§ăăžăăă äťăŽćšćłă§čŠąăăŚăăžăăăăăăă¨ăĺ ˇä˝çăŤă§ăăžăăďź
@mordrax
ăăăăăŁĺăăăšăŚćŁăăăă¨ă確čŞă§ăăžă
ăăăăŁăŚămapStateToPropsăĺ ŹéăăŚĺ ŹéăăăăšăăăContainerComponentăăŹăłăăŞăłă°ăăă¨ăăžăăăăăŤăŻăĺ˝ăŽăšăă˘ăŽé俥ăçść ăŽĺ°éĺ ˇăŽé俥ăĺŤăžăăžăă揥ăŤămapStateToPropsăŻăăŽçść ăĺăĺăăăăăĺ°éĺ ˇăŤăăăăăžăă ăăăăăăŽćçšăăăŠăŽăăăŤăăšăă§ăăžăăďź ConnectăŻmapStateToPropsăĺźăłĺşăăŚĺ°éĺ ˇăăăźă¸ăăžăăăăăŹăźăłăăźăˇă§ăłăłăłăăźăăłăăŤč¨ĺŽăăăŚăăĺ°éĺ ˇăă¤ăłăżăźăťăăă§ăăăăăťăš/ăăăźä¸ăŽăłăźăăŽçśăçŽăŻăŠăăŤăăăăŠăŽăă¤ăłăăçśăçŽă§ăăďź @ guatedude2ăŽăăăŞäşéăŽćľ çŹăŻăçśăçŽăŞăă§ăăăăăăĺ°éĺ ˇăăă§ăăŻăă1ă¤ăŽćšćłăăăăăŞăă¨ćăăžă...
ăăŞăăŽshallow.shallowăŤăă ă§ăŻăconnectďźďźăłăłăăźăăłăăŻä˝ăčżăăžăăăăŠăăăźă§ăăăďź ăăăŚăăăŽăŠăăăźăŻăăăŹăźăłăăźăˇă§ăłăłăłăăźăăłăăăŠăăăăăăŽă§ăăďź
@granmoeăŻăăăŞăăćŁç˘şăŤä˝ăćĺłăăăăŤă¤ăăŚăăăăăă芳細ăŤčŞŹćă§ăăžăăďź
connectăŤć¸Ąăé˘ć°ăăŤăăźăăŚăăĺ ´ĺăŻăăłăłăăźăăłăĺ ¨ä˝ăĺŽĺ ¨ăŤăŤăăźăăŚăăĺż čŚăăăăžăă
ç§ăŻăžăăăăšăăăă_what_ă¨ăăŽççąăŤé˘ăăŚ@tugoreză¨ä¸çˇăŤăăžăă
@markeriksonăšăă¤ă¨ăŽă¨ăŚăčŻăäžă ăăăŻăăšăă§ăăăă¨ăŽ1ă¤ă§ăăăăĺä˝ăŽĺä˝ăăăăšăăăăŤăŻăăăăŤĺ¤ăăŽă˘ăľăźăĺż čŚăŤăŞăăžăă ăšăă¤ă˘ăŻăˇă§ăłămapDispatchToPropsăăĺźăłĺşăăăăă¨ăăăšăăăă ăă§ăŻăăłăłăăăłăłăăźăăłăăŽĺ ¨ä˝ĺăŻăăăăžăăă ăłăłăăăŽăăłăăŠăă¸ăăŻăŤĺşăĽăăŚćĺž ăăăçľćă襨ćăăăă¨ăŻăăăžăăă
ĺ°éĺ ˇăçść ăŤĺć źăăăă¨ăăăšăăăă ăă§ăŻä¸ĺĺă§ăăăłăłăăăłăłăăźăăłăăŽĺä˝ăăăšăăăĺż čŚăăăăžăă ăăăŻă2ă¤ăŽăă¨ăăăšăăăăă¨ăćĺłăăžăă
1ďźăłăłăăăłăłăăźăăłăăŻĺ°éĺ ˇăăăŹăźăłăăźăˇă§ăłăłăłăăźăăłăăŤé çˇăăžăăăďźăăăăăŞăăĺ°éĺ ˇăăăăŹăźăłăăźăˇă§ăłăłăłăăźăăłăăŤć¸ĄăăăçšĺŽăŽçść ăŤĺşăĽăăŚăŹăłăăŞăłă°ăăăĺžăăăŹăźăłăăźăˇă§ăłăłăłăăźăăłăăćă¤ă¨äşćłăăăçšĺŽăŽçść ăăăăžăăďź čŞ°ăçĽăŁăŚăăăăăăăăä˝äşşăăŽćăăŞéçşč ăăăŁăŚćĽăŚmapStateToPropsăŤăłăźăăčż˝ĺ ăăžăăăăăăéŠĺăŤăăăăăăă¨ĺ¸¸ăŤäżĄé źă§ăăă¨ăŻéăăžăăăăăăăłăłăăăă¸ăăŻăŽăăšăăŽăă¤ăłăă§ăă mapStateToPropsăŤăŻĺŽéăŤăŻăă¸ăăŻăŻăăăžăăăăéçşč ăăăŁăŚćĽăŚăăăăŤifăšăăźăăĄăłăăă˘ăăăżă¤ăşăăăă¨ăçĽăŁăŚăăäşşăŻ...ăžăăăăăŻçŠäşăĺ°çĄăăŤăăĺŻč˝ć§ăŽăăĺä˝ă§ăă
2ďźăăŁăšăăăăăłăăŠăă¸ăăŻďźĺä˝ďźăŻăłăłăăĺ ă§ćŠč˝ăăžăăă
@dschinkel ďź
ReduxăŤćĽçśăăăăłăłăăźăăłăăĺŽçžŠăăăăăŽä¸čŹçăŞćšćłăŻă export default connect()(MyComponent)
ă¨ăĺĺäťăă¨ăŻăšăăźăă¨ăăŚexport MyComponent
ă使ç¨ăăăă¨ă§ăă ĺć§ăŤă mapState
ăŻĺăŞăé˘ć°ă§ăăăăă export const mapState = () => {}
ăă¤ăłăăźăăăŚăĺĺĽăŤăăšăăăăă¨ăă§ăăžăă
ăăłăłăăăłăłăăźăăłăăă¨ăăăŹăźăłăăźăˇă§ăłăłăłăăźăăłăăăŽăăšăăŤă¤ăăŚďźăăă§ăŽä¸čŹçăŞčăćšăŻăăăłăłăăă===ăăŽĺşĺăăŽăťă¨ăăŠăŽé¨ĺă§ăłăłăăăŽăăšăăŤă¤ăăŚĺżé
ăăĺż
čŚăŻăŞăă¨ăăăă¨ă§ăă connect
"ă React-ReduxăŤăŻăăăăăŠăŽăăăŤĺä˝ăăăăŤă¤ăăŚăŽĺŽĺ
¨ăŞăăšăăšă¤ăźăăăă§ăŤăăăăăŽĺŞĺăč¤čŁ˝ăăççąăŻăăăžăăă ăšăă˘ă¸ăŽăľăăšăŻăŠă¤ăă mapState
ăăăłmapDispatch
ăŽĺźăłĺşăăăăăłăŠăăăăăăłăłăăźăăłăă¸ăŽĺ°éĺ
ˇăŽĺă渥ăăćŁăăĺŚçăăăă¨ă_çĽăŁăŚăăžă_ă
ăŠă¤ăăŠăŞăŽĺŠç¨č
ă¨ăăŚăŽăăŞăăčĺłăćăŁăŚăăăŽăŻăăłăłăăźăăłăăŽĺä˝ă§ăă čŞĺăŽăłăłăăźăăłăăconnect
ăŠăăăźăăăšăăăžăăŻăăŽäťăŽăăŽăăĺ°éĺ
ˇăĺĺžăăŚăăăăŠăăăŻĺŽéăŤăŻĺéĄă§ăŻăăăžăăăçšĺŽăŽĺ°éĺ
ˇăŽăťăăăä¸ăăăăĺ ´ĺăăăŽăłăłăăźăăłăăăŠăŽăăăŤĺä˝ăăăăĺéĄăŤăŞăăžăă
ďźăžăăFWIWăăăŞăăŻçľśĺŻžăŤăăšăăŽĺ°é厜ă§ăăăç§ăŻăăă§ăŻăăăžăăăăăăŞăăŻçŠäşăŤă¤ăăŚĺ°ăăăŠăă¤ă˘ăŤăŞăŁăŚăăăăă§ă:) mapState
ăĺżé
ăŞă誤ăŁăŚĺŁăăĺ ´ĺăŻăăăšăăä˝ćăăŚćŹĄăŤé˛ăżăžăăďź
ăăă§ăăŠăăăăăăłăłăăźăăłăăăăŽĺ
é¨ă§äťăŽćĽçśăăăăłăłăăźăăłăăăŹăłăăŞăłă°ăăĺ ´ĺăçšăŤćľ
ăăŹăłăăŞăłă°ă§ăŻăŞăĺŽĺ
¨ăŞăŹăłăăŞăłă°ăčĄăĺ ´ĺăŻă const wrapper = mount(<Provider store={testStore}><MyConnectedComponent /></Provider>
ăĺŽčĄăăăă¨ă§ăăšăăăćšăăŻăăăŤç°Ąĺă§ăă ăăăăĺ
¨ä˝ă¨ăăŚăç§ăŽčăă§ăŻăăťă¨ăăŠăŽĺ ´ĺă mapState
é˘ć°ă¨ăăăŹăźăłăăłăłăăźăăłăăĺĽă
ăŤăăšăă§ăăăŻăă§ăăăćĽçśăăăăăźă¸ă§ăłă確čŞăăăăă ăăŤăăšăăăăšăă§ăŻăăăžăăă mapState
ăŽĺşĺăăăŹăźăłăłăłăăźăăłăăŤć¸ĄăăăŚăăăă¨ă
ĺčăžă§ăŤăDanăĺ
é¨ă§ä˝ăăăăă誏ćăăăăăŤăă°ăăĺăŤć¸ăăconnect
ăŽăăăăĽă˘ăăźă¸ă§ăłăčŚăă¨ăăă§ăăăďź https ďź//gist.github.com/gaearon/1d19088790e70ac32ea636c025ba424eă
ă¤ăžăămapStateToPropsă¨dispatchStateToPropsăă¨ăŻăšăăźăăăĺ ´ĺăŻăăăăčŻăă§ăăăă connectďźďźďźç§ăŽăăšăăŽĺ ąĺä˝ćĽč ďźăćŠč˝ăăăă¨ăăăšăăăăăăăžăăăééăăŞăăăă§ăŻăăăžăăă ăăăčĄăăăăŽć確ăŞćšćłăŽ1ă¤ăŻă揥ăŽ2ă¤ăŽăĄă˝ăăăă¨ăŻăšăăźăăăăă¨ă§ăă
example-container-spec.js
describe('testing mapPropsToState directly', () => {
it.only('returns expected state', () => {
const state = {firstName: 'Dave'};
const output = mapStateToProps(state);
expect(output.firstName).to.equal('Dave');
});
});
厚ĺ¨
import React from 'react';
import { connect } from 'react-redux'
import Example from './Example';
export const mapStateToProps = (state) => ({
firstName: state.firstName
})
export default connect(mapStateToProps)(Example);
ăăŹăźăłăăźăˇă§ăł
import React, { Component } from 'react';
export default class Example extends Component {
render(){
return (
<div className='example'>
{this.props.firstName}
</div>
)
}
}
揥ăŤă_shallow_ăŹăłăăŞăłă°ă使ç¨ăăŚćŹĄăŽăăăŞăă¨ăčĄăăĺăłăłăăźăăłăăŤéŁăłčžźăăă¨ăă§ăăžăă
example-container-spec.js
it('persists firstName to presentation component', () => {
var state = {firstName: "Dave"};
const container = mount(<ExampleContainer store={fakeStore(state)}/>),
example = container.find(Example);
expect(example.length).to.equal(1);
expect(example.text()).to.equal(state.firstName);
});
厚ĺ¨
import React from 'react';
import { connect } from 'react-redux'
import Example from './Example';
const mapStateToProps = (state) => ({
name: state.firstName
})
export default connect(mapStateToProps)(Example);
ăăŹăźăłăăźăˇă§ăł
import React, { Component } from 'react';
export default class Example extends Component {
render(){
return (
<div className='example'>
{this.props.firstName}
</div>
)
}
}
ăăăćľ ăăăăăăŤĺĽăŽćšćłăŻăăăăŤăˇăŁăăźăčĄăăă¨ă§ăă ćľ ăăŞăŁă茪ăćľ ăăăă¨ăĺăłăłăăźăăłăăćľ ăăŞăăăăŽăŠăăăłă°ăŻćŹĄăŽăăăŤăŞăăžăă
it('get child component by double shallow()', () => {
const container = shallow(<ExampleContainer store={fakeStore({})}/>),
presentationalChildComponent = container.find(Example).shallow().find('.example');
expect(presentationalChildComponent).to.have.length(1);
});
ă¤ăžăăăăăŻĺĽăŽćšćłă§ăăăăŹăźăłăăźăˇă§ăłăłăłăăźăăłăăŽçľćă確čŞăăŚăăăŽă§ăă
ăŠăĄăăŽćšćłă§ăćŠč˝ăăžă...ăă ăăăăŞăăč¨ăŁăăăăŤăă¨ăŻăšăăźăăăămapStateToPropsăŻăăăăćčŻă§ăăăăăŻăăăăăçść ăŽč¨ĺŽăŤé˘ăăŚăăšăăăăă¨ă ăăć°ăŤăăăăă§ăă
ç§ăŻăăŞăăăăšăăŽĺ°é厜ă§ăăăă¨ă羜寞ăŤç解ăăŚăăžăăăç§ăŻăăă§ăŻăăăžăăăăăăŞăăŻçŠäşăŤă¤ăăŚĺ°ăăăŠăă¤ă˘ăŤăŞăŁăŚăăăăă§ă:)
ăăăăăăă§ăŻăăăžăăăćŁăăăăšăăăă¨ăăăă¨ăŻăăăăăŞăă䞥ĺ¤ăćäžăăĺŞăăăăšăăčĄăăă¨ăéčŚă§ăă ăžă珏ä¸ăŤă誰ă_ĺ°é厜_ă§ăŻăăăžăăă 誰ăăçśçśçăŤĺŚçżăăŚăăăă¨ă´ăŽăăăŤăăăčŞăăăăŞăäşşăăăžăă ăăăŻă˝ăăăŚă§ă˘čˇäşşćďźĺĽĺăăăă§ăăˇă§ăăŞăşă ďźă¨ĺźă°ăăŚăăžăă
ăăŞăăč¨ăŁăăăăŤăćŻăčăă ăăăăšăăăĺ ąĺä˝ćĽč ďźconnectďźďźčŞä˝ďźăăăšăăăŞăăă¨ăéčŚă§ăă ăăăĺăăŚç解ăăăăžăăăšăăăăă¨ăéčŚă§ăă ĺ ąĺä˝ćĽč ăăăšăăăŚăăŞăăă¨ă確čŞăăăă¨ăŻéčŚă§ăăăăăăŻćăŤăŻäť˛éăŽéçşč ăŽéă§ăŽč°čŤăĺż čŚă¨ăăžăă
čć§ăăšăăčĄăăă¨ăă§ăăžăăăčć§ăăšăăčĄăăŞăăă¨ăéčŚă§ăă ăăšăăŻçĺŁăŤĺăć˘ăăăăăšăă§ăăăăăăŻăăŞăăăăăŤćŁăăčżăĽăăŚăăăăŠăăăçśçśçăŤăă§ăăŻăăăă¨ăćĺłăăžăă ç§ăŻĺăŤčŞĺăŽćľăăç解ăăăă¨ăăŚăăžăă ç§ăŻTDDăŞăŽă§ăăŠăŽăăăŤĺ§ăăăăéčŚă§ăăăăăăŤăŻéŠĺăŞăăšăăéčŚă§ăă ăăăăŁăŚăReactăłăłăăźăăłăăăăšăăăăăžăăžăŞćšćłăç解ăăăă¨ăăă¨ăăŤă誰ăăĺŚćłçăă§ăăă¨ćăăŚăŻăŞăăžăăă
ć°ăăReactăăšăăŞăă¸ăăŞăçťĺ ´...
ĺŽéăreact-reduxăăăšăăăăăăŽăăžăăžăŞăšăżă¤ăŤă¨çľăżĺăăăăăăłă˘ăăăźăă示ăăŞăă¸ăăŞăăăăŤĺ
ąćăăäşĺŽă§ăă ăăăčăăŚăăăŽăŻç§ă ăă§ăŻăŞăăŽă§ăäşşă
ăŽĺŠăăŤăŞăă¨ćăăžăă reduxăłăłăăăăăŽäťăŽăłăłăăźăăłăăŽăăšăăŤé˘ăăŚĺă質ĺăăăäşşăä˝ĺşŚăăăžăă ReduxăŽăăăĽăĄăłăăreact-reduxăŽăăăĽăĄăłăăăăăćŁĺ˝ĺăăăăŽă§ăŻăŞăăăăšăé ĺIMOăŽăăăĽăĄăłăăä¸čśłăăŚăăžăă ăăăŻčĄ¨é˘ăĺˇă¤ăăă ăă§ăăăReactăăšăă¸ăŽă˘ăăăźăăŤé˘ăăăăžăăžăŞăšăżă¤ăŤă示ăç´ ćľăŞăŞăă¸ăăŞăĺż
čŚăŞăŽă§ăăăăŤăăăć示ăăžăă
If anyone would like to contribute to that repo, please get a hold of me
ăŞăŽă§ăăłăŠăăŹăźăżăźăčż˝ĺ ă§ăăžăă äžăčż˝ĺ ăăŚăăăăăă§ăă React Test Utils + mocha ă Enzyme ă Ava ă Jest ă tapeăŞăŠă使ăŁăäžăčŚăŚăżăăă§ăă
çľčŤďź
ç§ăăĄăč°čŤăă严ćšăŽă˘ăăăźăăŻĺéĄăŞăă¨ćăăžăă ăĄă˝ăăăç´ćĽăăšăăăăăä¸č¨ăŽăăăŤăăšăăăŚă芳細ăŤčŞŹćăăžăă ĺŁăăăăăăšăăĺăăĺŻč˝ć§ăăăăăăçšĺŽăŽćšćłăŤĺşăĽăăŚăăšăăčĄăăăăŞăĺ ´ĺăăăăžăăăăŽăăăéĺťăŤăăšăăŤćŠăžăăăăŽăŻăŞăă§ăăă ăăăăŁăŚă1ă¤ăŽé˘ć°ăăăšăăăăăŠăăăăăăăăŚăăăăăăăšăăăăăŠăăăŻç°ăŞăăžăă ăĄă˝ăăăĺ ŹéăăăăŞăĺ ´ĺăăăăžăăăăŽä¸ă§ăłăłăăŠăŻăăăăšăăăăăŽä¸é¨ăéĺ ŹéăŤăăŚăăćšăăăĺ ´ĺăăăăžăă ăăăăŁăŚăăăšăăä˝ćăăă¨ăăŻăčŞĺăé ťçšăŤčĄăŁăŚăăăă¨ăčăăăă¨ă常ăŤéčŚă§ăă ăăăŻä˝ăćŞăăă¨ă§ăŻăăăžăăă čŻăăăšăăć¸ăăŽăŻç°Ąĺă§ăŻăăăžăăă
ăăăŻăžăăŤTDDăć¨é˛ăăŚăăăă¨ă§ăăăăăăé ťçšăŤčĄăŁăŚăăžăă ăăŽăăăăăăšăŞă ă§čĺźąć§ăŽä˝ăăłăźăăŤăŞăăžăă TDDăŻăĺžă§ă§ăŻăŞăăĺăăŁăŚăăšăăăăă¨ăčăăăăăŤĺźˇĺśăăžăă ăăăéăă§ăăăç§ăŽăăăŞäşşă ăăăžăăžăŞăăăźăă˘ăăăźăăç解ăăăă¨ćăççąăŻăTDDăĺŽčĄăăă¨ăăŤăč¨č¨ăĺ°ăăŞăăŁăłăŻă§é ťçšăŤĺčŠäžĄăăĺż čŚăăăăăă§ăăă¤ăžăăăăšăă˘ăăăźăăŤă¤ăăŚé ťçšăŤčăăĺż čŚăăăăžăă
@markeriksonăĺ Ľĺăăă ăăăăă¨ăăăăăžăăăăšăăŤă¤ăăŚčŠąăăŽă大弽ăă§ăă ăăäşşă ă ç§ăŻăăŞăăŽĺ°éçĽčăŤçĺăćăăăăŚăăžăăă§ăăăăă ç§ăĺŽéăŤmapStateToPropsăç´ćĽčŞĺă§ăăšăăăăă¨ăăăă¨ăăŞăăŁăă ăă§ăďź ăăšăżăźă§ăŞăăă¨ă§ćŞăăă¨ăăăŚăăăăă§ăŻăăăžăă;ďźă ç§ăŻĺăŤreduxăĺăăŚä˝żç¨ăăăŽă§ăă襨é˘ăŤč§Śăăăă ăă§ăŻăŞă質ĺăăăăžăă äşşă ăčŞĺăŽăăŚăăăă¨ăăăăăŠăŽăăăŤčĄăăă¨ăă§ăăăăăăăŚčŞĺăăĄăŽă˘ăăăźăăăĺŠçăĺžăŚăăăăŠăăăäşşă ăç解ă§ăăăăăŤăăăä˝ăăŹăăŤă§ăăšăăŤă¤ăăŚčŠąăĺăĺż čŚăăăăžăă ăăŽăăăŤăŻăćĽçśăreact-reduxăä¸ä˝ăŹăăŤăŽăăăă¤ăăźăç解ăăĺż čŚăăăăžăăăăăŤăăăăĺä˝ăŽăżăăăšăăăăćšćłăç解ă§ăăžăă mapStateToPropsăă¨ăŻăšăăźăăăŚăăäşşăŻăăžăăăžăăăăăăăŻç§ăŤă¨ăŁăŚăčĺłćˇąăăă¨ă§ă...ăăăŚăăŞăăăăăŞăăŽăä¸ćč°ăŤćăăžăă
WeDoTDD.com
ă¨ăăă§ăčĺłăŽăăäşşăŽăăăŤăç§ăŻć¨ĺš´WeDoTDD.comăĺ§ăăžăăďźReactbtwă§ć¸ăăăŚăăžăďźă çšĺŽăŽăăźă ďźçšĺŽăŽăăźă ăŽăăšăŚăŽéçşč
ďźăŽTDDăăžăăŻäźç¤žĺ
¨ä˝ďźä¸é¨ăŽäźćĽăŤăŻăăšăŚăŽTDDăçšăŤăłăłăľăŤăăŁăłă°äźç¤žăŽéçşč
ăăăžăďźăŽĺ ´ĺăŻă slack.wedotdd.comă§ç§ăŤéŁçľĄăăŚăă ăăă
ă˘ăăăăźăă
ćĽçśăăăăłăłăăźăăłăăăăšăăăç§ăŤă¨ăŁăŚä˝ăăăžăćŠč˝ăăŚăăăăćŻăčżăŁăĺžăç§ăŻ@markeriksonăŽăăă§ăŽĺŁ°ćăŤ100ďź ĺćăăžăă
ćĽçśăăăăłăłăăźăăłăăăăšăăăĺż čŚăăăă¨ĺŽéăŤćăăĺ ´ĺăŻăăŹăłăăŞăłă°ă§ăăăŻăă§ăă
ăăăă¤ăăźăăłăłăăăšăăŞăŠăŤă¤ăăŚĺżé ăăĺż čŚăŻăăăžăăă
ăăšăăŤ<Provider />
ăĺ°ĺ
Ľăăĺż
čŚăŻăăăžăăăăăăčĄăă¨ă <Provider />
ăćŠč˝ăăŚăăăă¨ăăăšăăăăŽă§ăŻăŞăăĺ
ąĺä˝ćĽč
ăăăšăăăŚăăăăăŤčŚăăžăă ăłăłăăźăăłăăŽĺä˝ăćŠč˝ăăăă¨ăăăšăăăĺż
čŚăăăăžăăăłăłăăăăłăłăăăšăăäťăăŚăšăă˘ăĺĺžăăŚăăăăŠăăăŻé˘äżăăăžăăă ăšăă˘ăĺ°éĺ
ˇă¨ăăŚć¸Ąăă ăă§ăăćĽçśăŤăŻăăšăă˘ăŤă˘ăŻăťăšăăăăăŽä˝ăăăŽćšćłăĺż
čŚă§ăăăăšăă§ăăăă¤ăăźăĺŽĺ
¨ăŤĺé¤ăăćšćłďźăłăłăăăšăăžăăŻĺ°éĺ
ˇďźăŻé˘äżăăăžăăă
ăžăăé ľç´ ăŽăłăłăăăšăăŞăăˇă§ăłă使ç¨ăăĺż čŚăŻăŞăă¨ćăăžăăăăăŻăReactăŽăłăłăăăšăăäťăăŚăšăă˘ăć°¸çśĺă§ăăăă1ă¤ăŽćšćłă§ăă ăăšăă§reactăŽăłăłăăăšăăĺŽĺ ¨ăŤĺżăăŚăă ăăăăăăŻăžăŁăăä¸čŚăŞčĽĺ¤§ĺă§ăăăăăšăăăăč¤éăŤăŞăăäżĺŽă¨čŞăżĺăăăŻăăăŤĺ°éŁăŤăŞăăžăă
mapStateToPropsç´ç˛é˘ć°ăăłăłăăăăĄă¤ăŤăŤă¨ăŻăšăăźăăăŚăç´ćĽăăšăăăžăă ç§ăŻmapStateToPropsăŽăăšăă¨ăăłăłăăăćľ ăăăšăăăăăă¤ăăŽăăšăăŽçľăżĺăăă使ç¨ăăŚăăăăăŹăźăłăăźăˇă§ăłăłăłăăźăăłăăĺ°ăŞăă¨ăćĺž ăăĺ°éĺ ˇăĺăĺăŁăŚăăăă¨ă確čŞăăŚăăăĺ°éĺ ˇăŽăăšăă§ĺć˘ăăžăă ăžăăăłăłăăăłăłăăźăăłăăŽăăšăă§ăŻäşéćľ ĺăčĄăăŞăăă¨ăŤăăžăăă TDDăŻăăăăăăă¨ăăă¨ăăšăăč¤éăŤăŞăăăăŚăăăăăä˝ăééăŁăăă¨ăăăŚăăă¨ăăăăŁăźăăăăŻăç§ăŤä¸ăăŚăăžăă ăăŽăééăŁăăă¨ăŻăăăłăłăăăłăłăăźăăłăăŽăăšăă§ćľ ăăăŽă2ĺăŤăăŞăăăă¨ă§ăă 䝣ăăăŤăăăŹăźăłăăźăˇă§ăłăłăłăăźăăłăăŽĺşĺăĺĺĽăŤăăšăăăăăŹăźăłăăźăˇă§ăłăłăłăăźăăłăăăšăăŽć°ăăăšă¤ăźăăä˝ćăăžăă
ăăŞăăçŠäşăăăžăăăŁăă¨čăăŚăăăăă§ăă ďźFWIWăç§ăŽăłăĄăłăăŻä¸ťăŤăçśăçŽăă¨ăĺ°éĺ ˇăćăä¸ăăăăŤă¤ăăŚăŽăăŞăăŽčłŞĺăŤçăăăăŽă§ăăă
<Provider>
/ context _is_ă使ç¨ăăĺż
čŚăăăăŽăŻăäťăŽćĽçśăăăăłăłăăźăăłăăăŹăłăăŞăłă°ăăăłăłăăźăăłăăŽĺŽĺ
¨ăŞăŹăłăăŞăłă°ăčĄăĺ ´ĺă§ăăăăăăŽćĽçśăăăĺăŻăăłăłăăăšăĺ
ăŽăšăă˘ăć˘ăăžăă
ăăšăăŤé˘ăăReduxăŽçžĺ¨ăŽăăăĽăĄăłăăćšĺăăăăăŽććĄăăăĺ ´ĺăŻăĺż ăPRăćĺşăăŚăă ăăă
ăăŚăłăăŽ@markeriksončŻăçšă ăă ăăăăšăăăŚăăăłăłăăźăăłăăŽăŠă¤ăăľă¤ăŻăŤăăăšăăăĺż čŚăăăĺ ´ĺăŻăăăŚăłăăŽăżăĺŽčĄăăžăă ĺăłăłăăźăăłăă桹ăćăä¸ăăăăăŤăăă使ç¨ăăăă¨ăŻăăăžăă...ăăăŻăăŻăĺä˝ăăšăă¨ăăŚéŠć źă§ăŻăŞăăĺşćŹçăŤçľąĺăăšăă§ăăăăăăăŽĺăłăłăăźăăłăă§ăăăăŽĺăłăłăăźăăłăăăăšăăăĺż čŚăăăĺ ´ĺăŻăăăŽăăšăăĺŽčŁ ăŽčŠłç´°ăŤçľĺăăžă茪ăłăłăăźăăłăăäťăăŚă§ăŻăŞăăĺçŹă§ććăăžăă
ă¨ăŤăăčŻăăăŽăăăăă¨ăďź
ă¤ăžăă mount
ă使ç¨ăăŚĺéĄăŽăłăłăăźăăłăăŽăŠă¤ăăľă¤ăŻăŤăăă§ăăŻăăăăŽăłăłăăźăăłăăäťăŽćĽçśăăăăłăłăăźăăłăăăŹăłăăŞăłă°ăăĺ ´ĺăă¨ăŠăźăĺéżăăăăăŤăăăăăŽăăšăăăăăłăłăăźăăłăăŽăłăłăăăšăă§ăšăă˘ă使ç¨ă§ăăăăăŤăăĺż
čŚăăăăžăăăăšă寞蹥ăŽăłăłăăźăăłăăăŹăłăăŞăłă°ăăăžăă
ďźçˇ¨éďźç§ăŻăŠăăăčŞĺčŞčşŤăçš°ăčżăăŚăăăăă§ăăçłă訳ăăăžăă-常ăŤăšăŹăăăćťăŁăăăĺ°ăä¸ăŤăšăŻăăźăŤăăăăăăŤăč¤ć°ăŽĺ ´ćă§čłŞĺăŤçăăĺąéşć§ăăăăžăăďź
ăăăăăăŁăďź ăăăăăăŤă¤ăăŚăŻčăăŚăăžăăă§ăă...
ăăăŤăăšăŚăŽăłăĄăłăăăăăă¨ăă @ markeriksonă¨@dschinkelďź ĺ˝źăăŻé常ăŤĺ˝šăŤçŤăĄăžăă ăăă§ăćĽçśăăăŚăăŞăăłăłăăźăăłăăă¨ăŻăšăăźăăăŚăäťăŽăłăłăăźăăłăă¨ĺăăăăŤăăšăăă mapStateToProps
ă¨mapDispatchToProps
ĺĽă
ăŤăăšăăăăă¨ăŤăăžăăă ăă ăăăăăăŽăăšăăć¤ĺşăăăŞăĺ ´ĺă1ă¤ăăăžăăăăăŻă map(State/Dispatch)ToProps
ăĺŽéăŤconnect
ăŽĺźăłĺşăăŤć¸ĄăăăŞăĺ ´ĺă§ăă äžďź
import React from 'react';
import { connect } from 'react-redux';
export const mapStateToProps = ({ name }) => ({ name });
export const Example = ({ name }) => <h1>{name}</h1>;
export default connect({ name } => ({ name: firstName }))(Example); // Whoops, didn't actually pass in `mapStateToProps`.
ăăăŻĺŽéăŤăŻăăăăçşçăăŞăă§ăăăăăçšăŤăŞăłăżăźăă¨ăŻăšăăźăăăăŚăăăăăćŞä˝żç¨ăŽĺ¤ć°ă¨ăăŚmapStateToProps
ăĺ ąĺăăŞăăăăçşçăăĺŻč˝ć§ăăăăžăă
@danny-andrewsmapDispatchToPropsé˘ć°ăăăšăăăćšćłăŤă¤ăăŚĺ ˇä˝çăŤćăăŚăă ăăă
@leizard
mapDispatchToPropsăžăăŻmapStateToPropsăç´ćĽăăšăăăŞăăŞăăžăăă ăăŽăšăŹăă䝼ćĽăç§ăŻčăăĺ¤ăăžăăăăăăăŞăăăăŤă˘ăăă¤ăšăăžăă
ăžăăăăăăăăšăăăă¨ă@ danny-andrewsăŽĺéĄăçşçăăžăăăĺéĄăŻéŠĺăŞăăšăăŤé˘ăăăăŽă§ăăăăăă2ă¤ăŽé˘ć°ăĺ Źéăăăă¨ăăăă¨ăŻăĺ§ăă§ăăžăăă ăăšăăçľăăŁăă 䝣ăăăŤăĺä˝ăăăšăăăăăćľ ăăłăłăăăäťăăŚĺ°éĺ ˇăăăšăăăăă¨ăŤăăăăăă2ă¤ăŽćšćłăéćĽçăŤăăšăăăĺż čŚăăăăžăă ăăăăŽăăŠă¤ăăźăăĄă˝ăăăĺ Źéăăăă¨ăăççąăŻăŞăăă¨ăăăăăĺ Źéăăăă¨ăăăŽăćŞăăăšăćšćłă§ăăăă¨ăŤć°ăĽăăžăăă
ă ăăç§ăŻ@markeriksonăŤĺćăăžăăăăăŞăăŽćĽçśăăăăłăłăăźăăłăăéăăŚăăšăăăŚăă ăăă
ăŞăźăăźăăšăă§ăŻăŞăăăăšăăăăŽăŤéŠĺăŞAPI/ăłăłăăŠăŻăăčŚă¤ăăăă¨ăéčŚă§ă:)ă
@dschinkel
ĺä˝ăăăšăăăăă¨ăŤăăăăăă2ă¤ăŽćšćłăéćĽçăŤăăšăăăĺż čŚăăăăžă...
ă ăăç§ăŻ@markeriksonăŤĺćăăžăăăăăŞăăŽćĽçśăăăăłăłăăźăăłăăéăăŚăăšăăăŚăă ăăă
ćĽçśăăăŚăăŞăăłăłăăźăăłăăĺŽĺ ¨ăŤă¨ăŻăšăăźăăăăŽăăăăćĽçśăăăăłăłăăźăăłăăŽăżăăăšăăăăă¨ăććĄăăŚăăžăăďź ăăăŻăéĺ°ăŞăăšăăă ă¨ćăăžăă ăăăŤăăăăăšăăăăšă寞蹥ăŽăłăłăăźăăłăăŽĺŽčŁ ăŽčŠłç´°ăŤä¸ĺż čŚăŤçľăłäťăăăăžăă ćĽçśăăăŚăăŞăăłăłăăźăăłăăŽćŠč˝ďźăăĺ ´ĺăŻăăăăŻăŻăźă ăŽäťăŽăăšăŚăŽçźśă§ăďźăŻăĺ°éĺ ˇăĺăĺăĺ ´ćă¨ăŻĺŽĺ ¨ăŤçĄé˘äżă§ăăăăă§ăă ĺ°ćĽçăŤăŻăăăŽăłăłăăźăăłăăç´ç˛ăŞăăŹăźăłăăźăˇă§ăłăłăłăăźăăłăăŤĺ¤ăăăă¨ćăăăăăăžăăă ćĽçśăăăăłăłăăźăăłăăăăšăăăĺ ´ĺăŻăăăšăŚăŽăăšăăĺ¤ć´ăăŚăăšăă˘ăćżĺ ĽăăăŤăĺ°éĺ ˇăç´ćĽć¸Ąăĺż čŚăăăăžăă ćĽçśăăăŚăăŞăăłăłăăźăăłăăăăšăăăĺ ´ĺăŻăćĽçśăăăăłăłăăźăăłăĺşćăŽăăšăăĺšăéŁă°ă䝼ĺ¤ăŤä˝ăăăĺż čŚăŻăăăžăăďźčŠłç´°ăŻäťĽä¸ăĺç §ďźă
ăă ăă mapStateToProps
/ mapDispatchToProps
ăç´ćĽăăšăăăăšăă§ăŻăŞăăă¨ăŤĺćăăžăă ăăăăŽăăŠă¤ăăźăăĄă˝ăăăĺăŤăăšăçŽçă§ĺ
Źéăăăă¨ăŻă常ăŤăłăźăăŽčăăŽăăăŤćăăžăăă ĺŽéăćĽçśăăăăłăłăăźăăłăăăăšăăăĺż
čŚăăăăŽăŻăăă ăă ă¨ćăăžăă čŚç´ăăă¨ă揥ăŽăăăŤăŞăăžăă
mapStateToProps
ăžăăŻmapDispatchToProps
ă¨ăŻăšăăźăăăŞăă§ăă ăă4çŞăĺŽčĄăăéăŽĺŻä¸ăŽä˝ĺăŞăŞăźăăźăăăăŻăćĽçśăăăăłăłăăźăăłăăŤć¸ĄăăăăŤreduxăšăă˘ăăšăżăă˘ăŚăăăĺż čŚăăăăă¨ă§ăă ăă ăăAPIăŻ3ă¤ăŽăĄă˝ăăăŤăăăŞăăăăăăăŻé常ăŤç°Ąĺă§ăă
TestContainer.jsxďź
import { connect } from 'react-redux';
export const TestContainer = ({ permissions }) => (permissions['view-message'] ? 'Hello!' : null);
export const mapStateToProps = ({ auth }) => ({ permissions: auth.userPermissions });
export default connect(mapStateToProps)(TestContainer); // Ewww, exposing private methods just for testing
TestContainer.spec.jsxďź
import React from 'react';
import { shallow } from 'enzyme';
import TestContainer, { mapStateToProps } from './TestContainer';
it('maps auth.userPermissions to permissions', () => {
const userPermissions = {};
const { permissions: actual } = mapStateToProps({
auth: { userPermissions },
});
expect(actual).toBe(userPermissions);
});
TestContainer.jsxďź
import { connect } from 'react-redux';
export const TestContainer = ({ permissions }) => (permissions['view-message'] ? 'Hello!' : null);
const mapStateToProps = ({ auth }) => ({ permissions: auth.userPermissions });
export default connect(mapStateToProps)(TestContainer);
TestContainer.spec.jsxďź
import React from 'react';
import { shallow } from 'enzyme';
import TestContainer, { TestComponent } from './TestContainer';
it('renders TestComponent with approriate props from store', () => {
const userPermissions = {};
// Stubbing out store. Would normally use a helper method. Did it inline for simplicity.
const store = {
getState: () => ({
auth: { userPermissions },
}),
dispatch: () => {},
subscribe: () => {},
};
const subject = shallow(<TestContainer store={store} />).find(TestComponent);
const actual = subject.prop('permissions');
expect(actual).toBe(userPermissions);
});
ăăăăăăŻç§ămapStateToPropsăă¨ăŻăšăăźăăăŚăăšăăăŞăă¨č¨ăŁăŚăăăă¨ă§ăăăăăŻăăšăăčś ăăŚăăžăă APIăäťăăŚăăšăăăžăă ăăă§ăŽAPIăŻăłăłăăă§ăă
@ danny-andrews
ćĽçśăăăŚăăŞăăłăłăăźăăłăăäťăăŚăăšăŚăŽăłăłăăźăăłăăă¸ăăŻăăăšăăăžă
ăăă¤ăăŽäžăç´šäťă§ăăžăăďź
ç§ăŻăăŞăăč¨ăŁăă¨ăă¨ĺăăă¨ăćĺłăăžăďźăăăŞăăŻčĄĺăăăšăăăăă¨ăŤăăŁăŚăăăăŽ2ă¤ăŽćšćłăéćĽçăŤăăšăăăăšăă§ăă
ç¨čŞăć確ăŤăăăŽăŤĺ˝šçŤă¤ăăăăăžăăăăćĽçśăăăŚăăŞăăłăłăăźăăłăăă¨č¨ăă¨ăăŻă connect
ăŽĺźăłĺşăăŤăŠăăăăăŚăăçăŽăłăłăăźăăłăăćĺłăăăćĽçśăăăŚăăăłăłăăźăăłăăă¨č¨ăă¨ăăŻăăăčżăăăçľćăŽăłăłăăźăăłăăćĺłăăžăă connect
ă¸ăŽĺźăłĺşăă ăéŁçľćĺăă¨ăăłăłăăăăŻĺ瞊čŞă ă¨ćăăžăă ç§ăŽä¸ăăďź
// Unconnected component. Test all component logic by importing this guy.
export const TestComponent = ({ permissions }) => (permissions['view-message'] ? 'Hello!' : null);
const mapStateToProps = ({ auth }) => ({ permissions: auth.userPermissions });
// Connected component (container). Only test interaction with redux by importing this guy.
export default connect(mapStateToProps)(TestComponent);
ä¸é¨ăŽäşşă
ăŻăăăăăĺŽĺ
¨ăŤĺĺ˛ăăăăłăłăăăăç´ç˛ăŞăăŹăźăłăăźăˇă§ăłăłăłăăźăăłăăăŠăăăăconnect
ă¸ăŽĺźăłĺşăăŤăăăă¨ă弽ăżăžăă ăăŽĺ ´ĺăăŞăšăăŽ1çŞă¨3çŞăŻăŞăăŞăăä˝ćăăĺż
čŚăŽăăăăšăăŻăreduxďź4çŞďźă¨ăŽç¸äşä˝ç¨ăć¤č¨źăăăăšăăŽăżăŤăŞăăžăă
ç§ăŻăăăŤă¤ăăŚĺˇ¨ĺ¤§ăŞăăă°ć稿ăć¸ăă¤ăăă§ăă ç§ăŻçžĺ¨ăłăźăă§ĺżăăă§ăăăĺžă§čżäżĄăăžă
ăăŽăšăŹăăăć°ĺčŞăă ĺžăç§ăŻ3ă¤ăŽă˘ăăăźăăć¨ĺĽ¨ăăăă¨ăăçľčŤăŤéăăžăăă
mapDispatchToProps
ă¨mapStateToProps
ăă¨ăŻăšăăźăăăĺĽă
ăŤăăšăăăžămapStateToProps
ăŽäťŁăăăŤăťăŹăŻăżăźă使ç¨ăă mapDispatchToProps
ăŽäťŁăăăŤă˘ăŻăˇă§ăłăŻăŞă¨ăźăżăźă使ç¨ăăŚăăăăăĺĺĽăŤăăšăăăžăă ă˘ăŻăˇă§ăłăŻăŞă¨ăźăżăźăăŹăăĽăźăľăźăăťăŹăŻăżăźăä¸çˇăŤä˝żç¨ăăŚăăšăăä˝ćăăăăăźĺ
¨ä˝ăćŠč˝ăăăă¨ă確čŞăăžăăçľĺąăăăšăŚăŽăŞăăˇă§ăłăŻćĺšă ă¨ćăăžăăăăăăăéˇćă¨çćăăăăžăă ăćăç´ç˛ăŞăăŻăăăă2çŞçŽăŽăăŽă§ăăăăťă¨ăăŠăŽä˝ćĽăĺŤăžăăžăă ç§ăŽĺäşşçăŞĺĽ˝ăżăŻăĺŽéăŤăŻăŞăăˇă§ăł3ăŤĺăăŁăŚăăžăă
ăăăŤă¤ăăŚăŻăăă°ăŤčŠłăăć¸ăăŚăăžăăăăłăźăăľăłăăŤăăăă¤ăăăăžăă
@ucorina
ES6ă˘ă¸ăĽăźăŤăŽçžĺ¨ăŽä¸çă§ăŻăĺăăĄă¤ăŤăŻăăčŞä˝ăŽăšăłăźăăŤăŠăăăăăă˘ă¸ăĽăźăŤă¨čŚăŞăăăžăă ă¨ăŻăšăăźăăŻĺ°ăăŞă˘ă¸ăĽăźăŤăŽAPIă§ăăăăĄă˝ăăăă¨ăŻăšăăźăăăăŽăŻé常ăŤééăŁăŚăăă¨ćăăžăăăăŽăăăăĄă˝ăăăăăšăă§ăăžăă ăăšăăăăăă ăăŤăăăŠă¤ăăźăăĄă˝ăăăăăăŞăăŻăŤăăćšćłăĺć§ă§ăă
ăăŽăăăç§ăŻ@dschinkelă˘ăăăźăăŤĺžĺăăŚăăămapDispatchă¨mapStateăă¨ăŻăšăăźăăăŚăăžăăăăăăŻăăăăăćĽçśăăăăłăłăăźăăłăăŽăăŠă¤ăăźăĺŽčŁ ă§ăăăăă§ăă
ăă ăăćĽçśăŽĺ¨ăăŤĺĽăŽăłăłăăźăăłăăăŠăăăăă ăăŽăłăłăăźăăłăăăŠăăăăăŻăăăăžăăă
ăăă°č¨äşăć¸ăăăă¨ăŻăăăžăăďź @dschinkelăŻăăăčŞăżăăă§ă
@AnaRobynn
ăăă mapDispatch
ă¨mapState
ăă¨ăŻăšăăźăăăă ăă§ăŻć°ĺăćŞăăăăăăčŞĺă§ăŻä˝żç¨ăăŞăă¨ćăăžăăăăç´ç˛ăă§ăŻăŞăăăŽăŽăćĺšăŞăŞăăˇă§ăłă§ăă ă ăăłăťă˘ăăŠă˘ăăăăŽćŁç˘şăŞăăŻăăăŻăăăă§ććĄăăăŽă§ăç§ăŻăăăĺŻč˝ăŞăŞăăˇă§ăłă¨ăăŚăăăŤćŽăăžăăďź https ://github.com/reduxjs/react-redux/issues/325#issuecomment-199449298ă
ăćĽçśăŽĺ¨ăăŤĺĽăŽăłăłăăźăăłăăăŠăăăăă ăăŽăłăłăăźăăłăăăŠăăăăăă¨ăă質ĺăŤçăăăăăŤăç§ăŻăăăăăšăăăŞăă¨č¨ăăžăă ă˘ăăŞăąăźăˇă§ăłăŤé˘éŁăăăă¸ăăšăă¸ăăŻăŻăŞăă connect
ĺŽčŁ
ăăăšăăăăăăăžăăăăăăŻreact-redux
ăŠă¤ăăŠăŞă§ăă§ăŤăăšăăăăŚăăžăă
ăăă§ăăăšăăăăĺ ´ĺăŻăăłăźăăĺ°ćĽăŤăăăŁăŚäżč¨źăăăăăŤă @dschinkelă誏ćăăŚăăăăŽăŤčżăăăă§čŞŹćăă2çŞçŽăŽă˘ăăăźăăŤĺžăăă¨ăă§ăăžăă
@ucorinaăžăăŤăç§ăăŞăăˇă§ăłă ă¨ćăăžăďź ăăŞăăŽçăăăă桹ă誏ćăăŚăăăŚăăăă¨ăă ăăžăć¸ăăăăăă°ć稿ă§ăă ăăă§ă¨ăďź
@ucorinaăăä¸ĺşŚă迡ćăăăăăăŚçłă訳ăăăžăăăăăă°ăăĺŻăĺžăăŞăăˇă§ăł2ăŤăĺŽĺ ¨ăŤĺćăăăăŠăăăŻăăăăžăăă ă§ăăăăăžăăă ăžăămapStateToPropsăăăšăăăăă¨ăŽĺŠçšăŤă¤ăăŚăăžăŁăăăăăăžăăă
ăăŞăăˇă§ăł2ăăéćĽçăŤă˘ăŻăˇă§ăłăŻăŞă¨ăźăżăźăăăšăăăăŽăŻčŻăă¨ćăăžăăďź ăžăăăťăŹăŻăżăźăéćĽçăŤăăšăăăžăăďź ăăăŻčŻăăă¨ă§ăăďź
ç§ăŻăăă§100ďź
ć罪ĺ¤ćąşăĺăăŚăăăăă§ăŻăăăžăăăăăăăŤăăăăšăŚăŽďźčޤăŁăďźć
ĺ ąăŤă¤ăăŚă桡䚹ăăŚăăžăă ăăšăăŤăŻé常ăŤĺ¤ăăŽç°ăŞăă˘ă¤ăă˘ăăăăžăă
mapStateToProps
ĺ
ăŽăťăŹăŻăżăźăŽăżă使ç¨ăăŚăă ăămapDispatchToProps
ĺ
ăŽă˘ăŻăˇă§ăłăŻăŞă¨ăźăżăźăŽăżă使ç¨ăăŚăă ăămapStateToProps
ă mapDispatchToProps
ăăžăăŻmergeProps
ăŤčż˝ĺ ăăĺż
čŚăăăăäťăŽĺ ´ćă§ăŻăžă ăăšăăăăŚăăŞăčż˝ĺ ăŽăă¸ăăŻăăăšăăăžăă ďźćĄäťśäťăăăŁăšăăăă ownProps
ăăĺźć°ăĺăăťăŹăŻăżăźăŞăŠďźĺĽďź
@AnaRobynn@ ucorinaăŽ2çŞçŽăŽă˘ăăăźăăćŁăăă¨ćăăžăă
ç§ăčăăăă¨ăŽĺ¤ăăŻćŹĄăŽăă¨ă示ĺăăŚăăžăă
1ăŤé˘ăăŚăŻăăăšăăŽăăăŤĺ é¨ăĺ ŹéăăăŽăŻčŻăçżć Łă§ăŻăŞăă¨ćăăžăă ăăšăAďźĺ é¨ć§é ăćłĺŽăăBďźĺŽéăŤăŠăŽăăăŤä˝żç¨ăăăăăăăšăăăĺż čŚăăăăžăă
2ăŤé˘ăăŚăŻăĺ¤é¨ăŠă¤ăăŠăŞăĺźăłĺşăăăŽă_羜寞ăŤ_ăăšăăăĺż čŚăăăăžăă ăăăŻăăăăăă˘ăăŞăąăźăˇă§ăłăŽç ´ĺŁăŽĺźąçšăŽ1ă¤ă§ăă ăŠăŽăŠă¤ăăŠăŞă§ăăăă¤ăăź/ăăăăăźă¸ă§ăłăŽăăłăă§é大ăŞĺ¤ć´ăĺ°ĺ ĽăăăĺŻč˝ć§ăăăăăăšăăăă°ăă夹ćăăăăă¨čăăŚăăžăă
ăăŞăăˇă§ăł2ăăéćĽçăŤă˘ăŻăˇă§ăłăŻăŞă¨ăźăżăźăăăšăăăăŽăŻčŻăă¨ćăăžăăďź ăžăăăťăŹăŻăżăźăéćĽçăŤăăšăăăžăăďź ăăăŻčŻăăă¨ă§ăăďź
ăŻăăĺéˇăŞăăšăăŤăăŹăă¸ăŻčŻăăă¨ă§ăă
@philihp @dougbacelar
ç§ăŻReactă˘ăăŞăąăźăˇă§ăłăŽăăšăăŤĺ°ăé˘éŁăăŚćéˇăăă¨ćăăžăă ç§ăŽçžĺ¨ăŽäżĄĺżľă¨ä¸ťéĄăŤă¤ăăŚăŽčăďź
mapStateToProps
ă¨mapDispatchToProps
ăĺ
ŹéăăŞăă§ăă ăăă ăăšăăŽăăă ăăŤăăăăă¨ăŻăšăăźăăăăă¨ăŻăă˘ăłăăăżăźăłă§ăă=>éŠĺăŞăăšăăŠă¤ăăŠăŞă使ç¨ăăă¨ăĺŽéăŽăăšăč¨ĺŽăŻé常ăŤç°ĄĺăŤăŞăăžăďźtestdouble.jsăăĺ§ăăăžăďźă
=> testdouble.jsăäťăăŚăťăŹăŻăżăźă¨ă˘ăŻăˇă§ăłăă˘ăăŻă˘ăŚăăăžăďź td.when()
ă使ç¨ďź
=>ăłăłăăăłăłăăźăăłăăćľ
ăăŹăłăăŞăłă°ăăžăăăăĽăźăłăłăăźăăłăăŽăĄă˝ăăăŤă˘ăŻăťăšăăăŤăŻă dive()
ă1ĺăŹăłăăŞăłă°ăăžăă
=> td.when()
ăŤăăä¸éŁăŽăŤăźăŤăä¸ăăăăĺ ´ĺăăłăłăăźăăłăăćŁăăĺä˝ăăăăŠăăăă˘ăľăźăăăžă
fakeStoreăŠă¤ăăŠăŞăćżĺ Ľăăĺż čŚăŻăăăžăăăăšăă˘ăăšăżăă˘ăŚăăăăŽăŻĺéĄăăăžăăă
äžďź
/** <strong i="27">@format</strong> */
import React from 'react';
import { shallow } from 'enzyme';
function setup(props) {
const HasOrganization = require('./HasOrganization').default;
const defaultProps = {
store: {
subscribe: Function.prototype,
getState: Function.prototype,
dispatch: Function.prototype,
},
};
const container = shallow(<HasOrganization {...defaultProps} {...props} />);
return {
container,
wrapper: container.dive(),
};
}
describe('<HasOrganization /> collaborations', () => {
let getCurrentOrganizationId;
beforeEach(() => {
getCurrentOrganizationId = td.replace('../containers/App/userSelectors')
.selectCurrentOrganizationId;
});
test('not render anything when the id is not valid', () => {
const Dummy = <div />;
td.when(getCurrentOrganizationId()).thenReturn(null);
const { wrapper } = setup({ children: Dummy });
expect(wrapper.find('div').length).toBe(0);
});
test('render something when the id is valid', () => {
const Dummy = <div />;
td.when(getCurrentOrganizationId()).thenReturn(1);
const { wrapper } = setup({ children: Dummy });
expect(wrapper.find('div').length).toBe(1);
});
});
čăďź
@AnaRobynnăă¤ăłă1ă¨2ăŤĺźˇăĺćăăžăďź
ç§ăŽăłăłăăăŻé常é常ăŤç°Ąĺă§ăďź
// imports hidden
const mapStateToProps = (state, { userId }) => ({
isLoading: isLoading(state),
hasError: hasError(state),
placeholders: getPlaceholders(userId)(state), // a list of some sort
shouldDoStuff: shouldDoStuff(state),
});
const mapDispatchToProps = {
asyncActionPlaceholder,
};
export const mergeProps = (stateProps, dispatchProps, ownProps) => {
return {
...stateProps,
...dispatchProps,
...ownProps,
onMount: () => {
if (stateProps.shouldDoStuff) {
dispatchProps.asyncActionPlaceholder(ownProps.userId);
}
},
};
};
export default compose(
connect(
mapStateToProps,
mapDispatchToProps,
mergeProps
),
callOnMount(props => props.onMount())
)(SampleComponent);
ă ăăç§ăŻ
getPlaceholders
ăšăă¤ăćŁăăuserIdă§ĺźăłĺşăăăžăăSampleComponent
ăćŁăăĺ°éĺ
ˇă§ăŹăłăăŞăłă°ăăăăă¨ă襨ćăăSampleComponent
onMount
shouldDoStuff===true
ăŽă¨ăăŤă˘ăăŻă˘ăŻăˇă§ăłăăăŁăšăăăăăăă¨ă確čŞăăžăSampleComponent
ăŽĺĽăŽăăšăă§ăăšăăăžăredux-mock-store
ă使ç¨ăăŚă configureMockStore([thunk])()
ăŽăăăŤăšăă˘ăă˘ăăŻăăăă¨ă弽ăżăžăăăăăăăŞăăŽăŻĺéĄăŞăă¨ćăăžă...TL; DRç§ăŻĺşćŹçăŤăăăšăăťăŹăŻăżăźăŽăżăćŁăăĺźć°ă§ĺźăłĺşăăăă˘ăăŻăăăă˘ăŻăˇă§ăłăćŁĺ¸¸ăŤăăŁăšăăăăăďźăă¸ăăŻăŤäžĺăăŚăăĺ ´ĺďźăĺăłăłăăźăăłăăćŁăăăăăăă§ăŹăłăăŞăłă°ăăăăă¨ă確čŞăăžăă
@philihp
@dougbacelar襨示ăăăłăłăăźăăłăăăăšăăăăăăŽăľăłăăŤăăšăăłăźăăćăăŚăă ăăă é ľç´ ă使ăŁăŚăăžăăďź äťăŤä˝ăăăăžăăďź ćľ ăďź ăăŚăłăďź
@dougbacelar
ç§ăŻĺ¤é¨ăŠă¤ăăŠăŞăŽăăšăăŤé常ăŤĺ寞ă§ăăăăšăŚăă˘ăăŻăăŚăçŠäşăĺĺĽăŤăăšăăăăă¨ă弽ăżăžăă
ĺż čŚăŞă¨ăăŤă˘ăăŻă˘ăăăăžăăăćŹçŠă使ç¨ă§ăăăŽă§ăăă°ăăŠăă§ăăăăă ă˘ăăŻăŻăçšĺŽăŽé芹ăé ăĺ ´ĺăăăăăăŻăźăŻčŚćąăŞăŠăŽĺŻä˝ç¨ăăăĺ ´ĺăŤĺ˝šçŤăĄăžăă
ăłăłăăăź+ăťăŹăŻăżăź+ă˘ăŻăˇă§ăłăŻăŞă¨ăźăżăźăăăšăŚä¸çˇăŤăăšăăăăăŞăççąăŻăăăăăĺĺŠç¨ăăäťăŽăăšăŚăŽăłăłăăăźăŤă¤ăăŚăĺăăťăŹăŻăżăźă¨ă˘ăŻăˇă§ăłăŻăŞă¨ăźăżăźăĺăăšăăăĺż čŚăăăăăă§ăă ăăŽćçšă§ăĺä˝ăăšăčŞä˝ăŤăăšăăąăźăšăčż˝ĺ ăăćšăăăă§ăăăă
ăŻăăăťăŹăŻăżăźăă˘ăŻăˇă§ăłăŻăŞă¨ăźăżăźăĺĺĽăŤăăšăăăžăă äşćłăăăĺ¤ăăŽĺ Ľĺă使ç¨ăăŚăăăăăĺžšĺşçăŤăăšăăăžăă
ăŻăăăłăłăăăăăšăăăžăă ăăăăťăŹăŻăżăźă¨ă˘ăŻăˇă§ăłăŻăŞă¨ăźăżăźăŤéč¤ăăăŤăăŹăă¸ăćäžăăăă¨ăćĺłăăĺ ´ĺăăăăŻćŞăăă¨ă§ăŻăăăžăăă
ăăăç§ăćĺłăăăă¨ă§ă...
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { saveColor } from '../actions/save';
import { selectColors } from '../reducers/colors.js';
import ColorButtons from '../components/ColorButtons';
const ColorButtons = ({ colors, onClick }) => (
<div>
{colors.map(color => {
<button type="button" key={color} onClick={onClick(color)}>{color}</button>
})}
</div>
);
ColorButtons.propTypes = {
colors: PropTypes.arrayOf(PropTypes.string),
onClick: PropTypes.func.isRequired,
};
const mapStateToProps = state => ({
colors: selectColors(state.colors),
});
const mapDispatchToProps = dispatch => ({
onClickColor: color => () => {
dispatch(saveColor({ color }));
},
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(ColorButtons);
import React from 'react';
import { shallow } from 'enzyme';
import configureStore from 'redux-mock-store';
import ColorButtons from '../ColorButtons';
import { saveColor } from '../../actions/save';
import { selectColors } from '../../reducers/colors.js';
const buildStore = configureStore();
describe('<ColorButtons />', () => {
let store;
let wrapper;
const initialState = { colors: ['red', 'blue'] };
beforeEach(() => {
store = buildStore(initialState);
wrapper = shallow();
});
it('passes colors from state', () => {
expect(wrapper.props().colors).toEqual(selectColors(initialState.colors));
});
it('can click yellow', () => {
const color = 'yellow';
wrapper.props().onClick(color)();
expect(store.getActions()).toContainEqual(saveColor({ color }));
});
});
ăăă§ăŻăăăăŻselectColors
ă¨saveColor
ăĺĺžă§ăăăăšăŚăŽç°ăŞăăăŠăĄăźăżăźăăăšăăăŚăăăăă§ăŻăăăžăăă <ColorButtons />
ăä˝ćăăăă¨ăćĺž
ăăăăăăăăŁăĺĺžăăăă¨ăăăšăăăŚăăžăă 羜寞ăŤĺĽăŽăăšăă§ăăăăăăšăăăŚăă ăăă
import { selectColors } from '../colors.js';
describe('selectColors', () => {
it('returns the colors', state => {
expect(selectColors(['red'])).toEqual(['red'])
})
it('returns an empty array if null', state => {
expect(selectColors(null)).toEqual([])
})
it('returns a flattened array', state => {
expect(selectColors(['red', ['blue', 'cyan']])).toEqual(['red','blue','cyan'])
})
})
ç§ăŻĺ¤é¨ăŠă¤ăăŠăŞăŽăăšăăŤé常ăŤĺ寞ă§ăăăăšăŚăă˘ăăŻăăŚăçŠäşăĺĺĽăŤăăšăăăăă¨ă弽ăżăžăă
ĺż čŚăŞă¨ăăŤă˘ăăŻă˘ăăăăžăăăćŹçŠă使ç¨ă§ăăăŽă§ăăă°ăăŠăă§ăăăăă ă˘ăăŻăŻăçšĺŽăŽé芹ăé ăĺ ´ĺăăăăăăŻăźăŻčŚćąăŞăŠăŽĺŻä˝ç¨ăăăĺ ´ĺăŤĺ˝šçŤăĄăžăă
é¨ĺçăŤĺćăăžăăă
ç§ăŻĺ¤é¨ăŠă¤ăăŠăŞăă˘ăăŻăăăă¨ăŻăăăžăăďźććăăŚăăŞăăăŽăă˘ăăŻăăŞăă§ăă ăăďźăăăă¨ăă°axiosăŽăŠăăăźăä˝ćăăžăă ăłăŠăăŹăźăˇă§ăłăăšăă§ăŻăăăšăŚăŽćŠč˝ăă˘ăăŻă˘ăŚăăăŚăăăšăŚăćŁăăćĽçśăăăŚăăăă¨ă確čŞă§ăăžăă
ăłăŠăăŹăźăˇă§ăłăăšăă§ăŻăă˘ă¸ăĽăźăŤăćŠč˝ăŞăŠă常ăŤă˘ăăŻăăžăă
ĺ
ąĺä˝ćĽč
ăŤăăŁăŚĺźăłĺşăăăăăăăŽé˘ć°ăŻăç°ĄĺăŤĺä˝ăăšăă§ăăžăă
ăăăç§ăćĺłăăăă¨ă§ă...
ăžăăĺćăăžăăă
ăăă§ăŻăăŹăăĽăźăľăźă¨ăťăŹăŻăżăźăćéťçăŤăăšăăăŚăăžăă ăăăăăŞăăăăžăăăăăăă¨ă§ăăăŞăăç§ăŻăăăăŽé˘ć°ăă˘ăăŻă˘ăŚăăăŚăăăăăćŁăăĺźăłĺşăăăŚăăăăŠăăăăă§ăăŻăăăă¨ă弽ăżăžăă ćŽăăŻăŹăăĽăźăľăź/ăťăŹăŻăżăźăŤăăŁăŚĺŚçăăăžăă
ăăŽăšăŹăăăŻă httpsďź //martinfowler.com/articles/mocksArentStubs.htmlăClassical andMockistTestingă§ä˝ćăăăăă¤ăłăăĺăăăˇăĽăăŚăăžăă
ĺćăăžăăă ăăăŻă
ćăĺčăŤăŞăăłăĄăłă
Reduxă¨ăŻç´ćĽé˘äżăăăžăăăă ăłăłăăă§
shallow()
ăĺ庌ĺźăłĺşăăă¨ă§ăăăă解湺ăăžăăă ăšăă˘çść ă渥ăăăĺ é¨ăłăłăăźăăłăăăŹăłăăŞăłă°ăăžăăäžďź
ă彚ăŤçŤăŚăă°