Last active
November 5, 2018 19:16
-
-
Save bootcoder/94ed3c73f54ff681fe8513874f50682e to your computer and use it in GitHub Desktop.
ReduxRootComponent Architecture
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ReduxRootComponent.tsx | |
import React from 'react' | |
import { Provider } from 'react-redux' | |
import WebpackerReact from 'webpacker-react' | |
import configureStore from '../store/configureStore' | |
const store = configureStore() | |
export function composeWithRedux (ReduxComponent, props) { | |
WebpackerReact.register(ReduxComponent.WrappedComponent) | |
return ( | |
<Provider store={store}> | |
<ReduxComponent {...props} /> | |
</Provider> | |
) | |
} | |
// SampleContainer.tsx - parent smart component which needs to connect to Redux | |
// This is the component which is rendered in the HAML. | |
// All rendered components which require state connect to Redux by composeWithRedux wrapper function. | |
import * as React from 'react'; | |
import { composeWithRedux } from '../ReduxRootComponent'; | |
import { connect } from 'react-redux'; | |
import { bindActionCreators } from 'redux'; | |
import SampleCard from './SampleCard'; | |
import * as sampleActions from '../../actions/sampleActions'; | |
import * as userActions from '../../actions/userActions'; | |
import './SampleContainer.scss'; | |
import 'bootstrap/dist/css/bootstrap.css'; | |
/* eslint-disable no-undef, no-unused-vars */ | |
type SampleContainerProps = { | |
message?: string; | |
cards: any[]; | |
actions: { [key: string]: any }; | |
selectedCardId: number; | |
}; | |
/* eslint-enable no-undef, no-unused-vars */ | |
export class SampleContainer extends React.Component<SampleContainerProps> { | |
constructor (props?: any) { | |
super(props); | |
this.renderCards = this.renderCards.bind(this); | |
this.handleAddCard = this.handleAddCard.bind(this); | |
this.handleRemoveCard = this.handleRemoveCard.bind(this); | |
this.handleLikeCard = this.handleLikeCard.bind(this); | |
this.handleSelectCard = this.handleSelectCard.bind(this); | |
} | |
handleAddCard () { | |
this.props.actions.addCard(); | |
} | |
handleLikeCard (cardId) { | |
this.props.actions.likeCard(cardId); | |
} | |
handleRemoveCard () { | |
this.props.actions.removeCard(); | |
} | |
handleSelectCard (cardId) { | |
this.props.actions.selectCard(cardId); | |
} | |
renderCards () { | |
return this.props.cards.map((card, idx) => { | |
return ( | |
<SampleCard | |
key={idx} | |
id={card.id} | |
value={card.value} | |
likesCount={card.likesCount} | |
handleLikeCard={this.handleLikeCard} | |
handleSelectCard={this.handleSelectCard} | |
selectedCardId={this.props.selectedCardId} | |
/> | |
); | |
}); | |
} | |
render () { | |
return ( | |
<div className='SampleContainer container'> | |
<div className='row'> | |
<h2> | |
SampleContainer Component Supreme! | |
</h2> | |
<p>Message: {this.props.message}</p> | |
<div className='btn-group'> | |
<button | |
className='btn btn-success handleAddCard' | |
onClick={this.handleAddCard}> | |
Add New Card! | |
</button> | |
<button | |
className='btn btn-warning handleRemoveCard' | |
onClick={this.handleRemoveCard}> | |
Remove Selected Card. | |
</button> | |
</div> | |
</div> | |
<div className='row'>{this.renderCards()}</div> | |
</div> | |
); | |
} | |
} | |
function mapDispatchToProps (dispatch) { | |
return { | |
actions: bindActionCreators(Object.assign({}, sampleActions, userActions), dispatch) | |
}; | |
} | |
const mapStateToProps = state => { | |
return { | |
user: state.user.userPayload, | |
message: state.sample.message, | |
selectedCardId: state.sample.selectedCardId, | |
cards: state.sample.cardCollection | |
}; | |
}; | |
const comp = connect( | |
mapStateToProps, | |
mapDispatchToProps | |
)(SampleContainer); | |
let ReduxSampleContainer = props => composeWithRedux(comp, props); | |
export default ReduxSampleContainer; | |
// SampleContainer.test.tsx | |
import WebpackerReact from 'webpacker-react'; | |
import * as React from 'react'; | |
import { mount } from 'enzyme'; | |
import SampleContainer from './SampleContainer'; | |
import SampleCard from './SampleCard'; | |
function setup () { | |
const comp = mount(<SampleContainer />); | |
console.log(comp); | |
debugger; | |
return comp; | |
} | |
describe('<SampleContainer />', () => { | |
let wrapper; | |
beforeEach(() => { | |
wrapper = setup(); | |
}); | |
afterEach(() => { | |
wrapper.unmount(); | |
WebpackerReact.registeredComponents = {}; | |
}); | |
it('passes smoke test', () => {}); | |
it('renders card components', () => { | |
expect(wrapper).toContainMatchingElements(1, 'SampleCard'); | |
expect(wrapper.find(SampleCard).first()).toHaveProp('value'); | |
expect(wrapper.find(SampleCard).first()).toHaveProp('id'); | |
expect(wrapper.find(SampleCard).first()).toHaveProp('likesCount'); | |
expect(wrapper.find(SampleCard).first()).toHaveProp('handleLikeCard'); | |
expect(wrapper.find(SampleCard).first()).toHaveProp('handleSelectCard'); | |
}); | |
it('adds a new card', () => { | |
expect(wrapper).toContainMatchingElements(1, 'SampleCard'); | |
wrapper.find('.handleAddCard').simulate('click'); | |
expect(wrapper).toContainMatchingElements(2, 'SampleCard'); | |
}); | |
// Fails due to the Container state already having a SampleCard when test begins. | |
it('remove a card', () => { | |
expect(wrapper).toContainMatchingElements(1, 'SampleCard'); | |
wrapper.find('.handleRemoveCard').simulate('click'); | |
expect(wrapper).toContainMatchingElements(0, 'SampleCard'); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment