Skip to content

Instantly share code, notes, and snippets.

@yelouafi
Last active October 5, 2016 07:13
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yelouafi/a245691d02c39cb244487bf6f42b5097 to your computer and use it in GitHub Desktop.
Save yelouafi/a245691d02c39cb244487bf6f42b5097 to your computer and use it in GitHub Desktop.
redux-saga with vanilla React
import React, { Component } from 'react'
import { runSaga, delay } from 'redux-saga'
import { take, put, call } from 'redux-saga/effects'
// helper functions
const bindFunc = (comp, method) => (...args) => method.apply(comp, args)
const bindSaga = (comp, method) => (...args) => runSaga(method.apply(comp, args), {})
export default class Counter extends React.Component {
constructor() {
super()
this.state = {count: 0}
this.increment = bindFunc(this, this.increment)
this.decrement = bindFunc(this, this.decrement)
this.incrementAsync = bindSaga(this, this.incrementAsync)
}
increment() {
this.setState({count: this.state.count + 1})
}
decrement() {
this.setState({count: this.state.count - 1})
}
*incrementAsync() {
yield call(delay, 1000)
yield call(this.increment)
}
render() {
return (
<p>
Clicked: {this.state.count} times
{' '}
<button onClick={this.increment}>
+
</button>
{' '}
<button onClick={this.decrement}>
-
</button>
{' '}
<button onClick={this.incrementAsync}>
Increment async
</button>
</p>
)
}
}
import React, { Component } from 'react'
import { runSaga, channel, takeLatest, delay } from 'redux-saga'
import { put, call, race } from 'redux-saga/effects'
import api from '..'
// You'll likely want to split this into Container/Presentation components
export default class ProductList extends React.Component {
constructor() {
super()
this.state = {
products: [],
isFetching: false,
fetchError: ''
}
// W'll use this channel to dispatch messages to our Saga from event callbacks
this.channel = channel()
}
// run the root saga
componentWillMount() {
this._task_ = runSaga(this.watchFetchProducts(), {
dispatch: data => this.setState(data) // yield put({...}) => this.setState({...})
})
}
componentWillUnmount() {
// cancel any pending fetch
this._task_.cancel()
}
*fetchProducts(timeout) {
yield put({isFetching: true, fetchError: ''}) // this will resolve to setState({isFetching: true, ...})
try {
const winner = yield race({
response: call(api.getProducts),
timeout: call(delay, timeout)
})
if(winner.response) {
yield put({products: winner.response, isFetching: false})
} else {
throw 'Request timeout!'
}
} catch(error) {
yield put({isFetching: false, fetchError: error})
}
}
*watchFetchProducts() {
yield* takeLatest(this.channel, this.fetchProducts.bind(this))
}
render() {
return (
<div>
<button onClick={() => this.channel.put(1000 /*timeout*/)}>Fetch products</button>
{ this.state.isFetching ?
<p>Fetching products ...</p>
: this.state.fetchError ?
<p>{this.state.fetchError}</p>
: /* has users */
<ul>{
this.state.products.map(product =>
<li key={product.id}>
{product.title}
</li>
)
}</ul>
}
</div>
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment