Skip to content

Commit

Permalink
Refactor the redux/redux-saga example to include error handling and b…
Browse files Browse the repository at this point in the history
…etter naming
  • Loading branch information
Matthieu Napoli committed Aug 13, 2018
1 parent b700f91 commit 739d3cd
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 19 deletions.
9 changes: 6 additions & 3 deletions App/Containers/HomeScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const instructions = Platform.select({

class HomeScreen extends React.Component {
componentDidMount() {
this.props.refreshTemperature()
this.props.fetchTemperature()
}

render() {
Expand All @@ -23,6 +23,7 @@ class HomeScreen extends React.Component {
<Text style={styles.instructions}>
The weather temperature is: {this.props.temperature}
</Text>
<Text style={styles.instructions}>{this.props.errorMessage}</Text>
</View>
)
}
Expand All @@ -47,14 +48,16 @@ const styles = StyleSheet.create({

HomeScreen.propsTypes = {
temperature: PropTypes.number,
errorMessage: PropTypes.string,
}

const mapStateToProps = (state) => ({
temperature: state.example.get('weatherTemperature'),
temperature: state.example.get('temperature'),
errorMessage: state.example.get('errorMessage'),
})

const mapDispatchToProps = (dispatch) => ({
refreshTemperature: () => dispatch(ExampleActions.requestWeatherTemperature()),
fetchTemperature: () => dispatch(ExampleActions.fetchTemperature()),
})

export default connect(
Expand Down
17 changes: 12 additions & 5 deletions App/Sagas/ExampleSaga.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { put } from 'redux-saga/effects'
import { put, call } from 'redux-saga/effects'
import ExampleActions from 'App/Stores/Example/Actions'
import { WeatherService } from 'App/Service/WeatherService'

/**
* A saga can contain multiple functions.
*
* This example saga contains only one to fetch the weather temperature.
*/
export function* fetchWeatherTemperature() {
// Fetch the temperature from an API (in this example we hardcode 24 degrees)
const temperature = 24
export function* fetchTemperature() {
// Fetch the temperature from an API
const temperature = yield call(WeatherService.fetchTemperature)

// Dispatch a redux action using `put()`
// @see https://redux-saga.js.org/docs/basics/DispatchingActions.html
yield put(ExampleActions.updateWeatherTemperature(temperature))
if (temperature) {
yield put(ExampleActions.fetchTemperatureSuccess(temperature))
} else {
yield put(
ExampleActions.fetchTemperatureFailure('There was an error while fetching the temperature.')
)
}
}
6 changes: 3 additions & 3 deletions App/Sagas/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { takeLatest } from 'redux-saga/effects'
import { ExampleTypes } from 'App/Stores/Example/Actions'
import { fetchWeatherTemperature } from './ExampleSaga'
import { fetchTemperature } from './ExampleSaga'

export default function* root() {
yield [
/**
* @see https://redux-saga.js.org/docs/basics/UsingSagaHelpers.html
*/
// Call `fetchWeatherTemperature()` when a `REQUEST_WEATHER_TEMPERATURE` action is triggered
takeLatest(ExampleTypes.REQUEST_WEATHER_TEMPERATURE, fetchWeatherTemperature),
// Call `fetchTemperature()` when a `FETCH_TEMPERATURE` action is triggered
takeLatest(ExampleTypes.FETCH_TEMPERATURE, fetchTemperature),
]
}
1 change: 1 addition & 0 deletions App/Service/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This directory contains services that are meant to connect the application to other APIs, for example
28 changes: 28 additions & 0 deletions App/Service/WeatherService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { create } from 'apisauce'

const weatherApiClient = create({
baseURL: 'https://query.yahooapis.com/v1/public/',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
timeout: 3000,
})

function fetchTemperature() {
const locationQuery = escape(
"select item.condition.temp from weather.forecast where woeid in (select woeid from geo.places where text='Lyon, Rhone-Alpes, FR') and u='c'"
)

return weatherApiClient.get('yql?q=' + locationQuery + '&format=json').then((response) => {
if (response.ok) {
return response.data.query.results.channel.item.condition.temp
}

return null
})
}

export const WeatherService = {
fetchTemperature,
}
10 changes: 6 additions & 4 deletions App/Stores/Example/Actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ import { createActions } from 'reduxsauce'
* @see https://github.com/infinitered/reduxsauce#createactions
*/
const { Types, Creators } = createActions({
// Request to fetch the current weather temperature
requestWeatherTemperature: null,
// Update the current weather temperature (after it was fetched)
updateWeatherTemperature: ['temperature'],
// Fetch the current weather temperature
fetchTemperature: null,
// The temperature was successfully fetched
fetchTemperatureSuccess: ['temperature'],
// An error occurred
fetchTemperatureFailure: ['errorMessage'],
})

export const ExampleTypes = Types
Expand Down
3 changes: 2 additions & 1 deletion App/Stores/Example/InitialState.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ import { Map } from 'immutable'
* The initial values for the redux state.
*/
export const INITIAL_STATE = Map({
weatherTemperature: null,
temperature: null,
errorMessage: null,
})
17 changes: 14 additions & 3 deletions App/Stores/Example/Reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,25 @@ import { ExampleTypes } from './Actions'
/**
* Example of a reducer that updates the `temperature` property.
*/
export const updateWeatherTemperature = (state, { temperature }) =>
export const updateTemperature = (state, { temperature }) =>
state.merge({
weatherTemperature: temperature,
temperature: temperature,
errorMessage: null,
})

/**
* Example of a reducer that updates the `errorMessage` property.
*/
export const showErrorMessage = (state, { errorMessage }) =>
state.merge({
temperature: '??',
errorMessage: errorMessage,
})

/**
* @see https://github.com/infinitered/reduxsauce#createreducer
*/
export const reducer = createReducer(INITIAL_STATE, {
[ExampleTypes.UPDATE_WEATHER_TEMPERATURE]: updateWeatherTemperature,
[ExampleTypes.FETCH_TEMPERATURE_SUCCESS]: updateTemperature,
[ExampleTypes.FETCH_TEMPERATURE_FAILURE]: showErrorMessage,
})
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"rename": "react-native-rename"
},
"dependencies": {
"apisauce": "^0.15.2",
"immutable": "^3.8.2",
"prop-types": "^15.6.1",
"react": "16.3.1",
Expand Down
24 changes: 24 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,13 @@ anymatch@^2.0.0:
micromatch "^3.1.4"
normalize-path "^2.1.1"

apisauce@^0.15.2:
version "0.15.2"
resolved "https://registry.yarnpkg.com/apisauce/-/apisauce-0.15.2.tgz#89c8dfd353bf2f3202a59d7f817eba7ec1c3b0dd"
dependencies:
axios "^0.18.0"
ramda "^0.25.0"

append-transform@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab"
Expand Down Expand Up @@ -722,6 +729,13 @@ aws4@^1.6.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289"

axios@^0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.0.tgz#32d53e4851efdc0a11993b6cd000789d70c05102"
dependencies:
follow-redirects "^1.3.0"
is-buffer "^1.1.5"

babel-code-frame@^6.22.0, babel-code-frame@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
Expand Down Expand Up @@ -2496,6 +2510,12 @@ flat-cache@^1.2.1:
graceful-fs "^4.1.2"
write "^0.2.1"

follow-redirects@^1.3.0:
version "1.5.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.2.tgz#5a9d80e0165957e5ef0c1210678fc5c4acb9fb03"
dependencies:
debug "^3.1.0"

for-in@^1.0.1, for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
Expand Down Expand Up @@ -4653,6 +4673,10 @@ ramda@^0.24.1:
version "0.24.1"
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857"

ramda@^0.25.0:
version "0.25.0"
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.25.0.tgz#8fdf68231cffa90bc2f9460390a0cb74a29b29a9"

ramdasauce@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/ramdasauce/-/ramdasauce-2.1.0.tgz#65ea157a9cfc17841a7dd6499d3f9f421dc2eb36"
Expand Down

0 comments on commit 739d3cd

Please sign in to comment.