Created
September 5, 2017 16:04
-
-
Save EloB/c01373bbc1afb96c96b9728294b1dfe7 to your computer and use it in GitHub Desktop.
React, redux and rxjs example
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
import React from 'react'; | |
import PropTypes from 'prop-types'; | |
import { Observable } from 'rxjs'; | |
import shallowEqual from 'shallowequal'; | |
import RxComponent from './RxComponent'; | |
const connectRx = handleStream => ComposedComponent => class ConnectRx extends RxComponent { | |
static contextTypes = { | |
store: PropTypes.object.isRequired, | |
}; | |
engine() { | |
const { store } = this.context; | |
const state$ = Observable | |
.create((observer) => { | |
observer.next(store.getState()); | |
return store.subscribe(() => observer.next(store.getState())); | |
}) | |
.publishReplay(1) | |
.refCount(); | |
return handleStream( | |
state$, | |
this.props$, | |
action => store.dispatch(action), | |
) | |
.distinctUntilChanged(shallowEqual) | |
.map(props => <ComposedComponent {...props} />); | |
} | |
}; | |
export default connectRx; |
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
import { Subject } from 'rxjs'; | |
const createEventStream = () => { | |
const subject = new Subject(); | |
return { | |
stream$: subject.map(e => e), | |
handler: e => subject.next(e), | |
}; | |
}; | |
export default createEventStream; |
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
import createEventStream from './createEventStream'; | |
import connectRx from './connectRx'; | |
const SmartComponent = connectRx((state$, props$, dispatch) => { | |
const { stream$: streamSubmit$, handler: onSubmit } = createEventStream(); | |
const router$ = props$.pluck('router'); | |
return streamSubmit$ | |
.switchMap((e) => Observable | |
.from(dispatch(someSyncAction())) | |
.concat(router$.first().do(router => router.push('/some/url'))) | |
) | |
.ignoreElements() | |
.merge(state$) | |
.combineLatest(props$, (state, ownProps) => ({ | |
name: state.user[ownProps.userId], | |
onSubmit | |
})) | |
})(DumbComponent); |
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
import { Component, Children } from 'react'; | |
import { ReplaySubject, Subject } from 'rxjs'; | |
class RxComponent extends Component { | |
state = { | |
component: null, | |
}; | |
componentWillMount() { | |
this.props$.next(this.props); | |
this.subscription = this.clientMounted$ | |
.ignoreElements() | |
.merge(this.engine()) | |
.subscribe( | |
component => this.setState({ error: null, component }), | |
error => this.setState({ error }), | |
); | |
} | |
componentDidMount() { | |
this.didMountSubject.next(true); | |
} | |
componentWillReceiveProps(props) { | |
this.props$.next(props); | |
} | |
shouldComponentUpdate(nextProps, nextState) { | |
return this.state.component !== nextState.component; | |
} | |
componentWillUnmount() { | |
this.subscription.unsubscribe(); | |
} | |
engine() { // eslint-disable-line | |
throw new Error('Should be implemented'); | |
} | |
props$ = new ReplaySubject(1); | |
didMountSubject = new Subject(); | |
clientMounted$ = this.didMountSubject | |
.startWith(false) | |
.publishReplay(1) | |
.refCount(); | |
render() { | |
const { component, error } = this.state; | |
if (error) throw error; | |
return component ? Children.only(component) : null; | |
} | |
} | |
export default RxComponent; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I been having some problems with timing/teardown issues for
react-redux
. If you been using for instanceredux-thunk
orredux-api-middleware
or just doing some async actions withreact-redux
I think you can benefit from my code above.Here is an example of problematic code with the current
react-redux
. You do some async action and when it resolves you want to redirect to another path. This is of course doable withredux-api-middleware
but what happens if your component unmounts? Then you probably want to teardown that behavior.