Sunteți pe pagina 1din 8

React Redux Saga example app.

Ron Lavit Follow


Jun 25, 2018 · 5 min read

Click Here to see a live example of what we’ll be building.

Redux-saga is a redux middleware library, that is designed to make


handling side effects in your redux app nice and simple. It achieves this
by leveraging an ES6 feature called Generators, allowing us to write
asynchronous code that looks synchronous, and is very easy to test.

So why should we use redux-saga? Contrary to redux thunk, you don’t


end up in callback hell, you can test your asynchronous flows easily and
your actions stay pure.

In this very basic App I have only one button which can make a call to
the NEWS API . This call requests only one news from the CNN channel
-the top one.

The App has generally three state :

STATE(before call) →STATE(during call) →STATE(answer received)

Let’s have a quick look at the file structure of my example.

src/
components/
App.js
containers/
Button.js
NewsItem.js
Loading.js
actions/
index.js
reducers/
index.js
sagas/
index.js
index.html
index.js
loading_spiner.gif

I presume you know how to set up a development environment and


install all the necessary dependencies . Thus I am not going into details
about that.

Now lets go step by step .

index.js

import React from 'react';


import createSagaMiddleware from 'redux-saga';
import { render } from 'react-dom';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import { logger } from 'redux-logger';
import reducer from './reducers';
import App from './components/App';
import rootSaga from './sagas';

const sagaMiddleware = createSagaMiddleware();

const store = createStore(


reducer,
applyMiddleware(sagaMiddleware, logger),

);

sagaMiddleware.run(rootSaga);

render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'),
);

In this file I’ve initialized redux store . This store uses two middlewares
logger and SAGA. Redux-logger tool to inspect in console panel
triggered actions and state of Redux store.

APP.js
import React from 'react';
import Button from '../containers/Button';
import NewsItem from '../containers/NewsItem'
import Loading from '../containers/Loading'

let App = () => (


<div>
<Button />
<Loading />
<NewsItem />
</div>
);

export default App;

This file is quite simple. It’s shows only three component.In the first
state it’s displays only a button , during a call to API the spinner
appears, and after the response have received <NewsItem /> is being
shown.

Button.js

import React from 'react';


import { connect } from 'react-redux';
import { getNews } from '../actions';

let Button=({getNews})=>(
<button onClick={getNews}>Press to see news</button>
)

const mapDispatchToProps = {
getNews: getNews,
};

Button = connect(null,mapDispatchToProps)(Button);

export default Button;

<Button /> component displays just button. I connect this component


to Redux using connect function from react-redux. onClick method of
this component triggers getNews action,which I am going to write next.

actions/index.js
export const getNews = () => ({
type: 'GET_NEWS',
});

Action creator getNews is very simple .It returns an object with only type
of action.

And let a reducer know how to deal with this type of actions.By now
reducer will looks like this :

reducers/index.js

const reducer = (state = {}, action) => {


switch (action.type) {
case 'GET_NEWS':
return { ...state, loading: true };
default:
return state;
}
};

export default reducer;

When action ‘GET_NEWS’ was dispatched property of state loading


becomes equal to true and the spinner is appears on the screen.And of
course call to API should be triggered and after response have arrived
action ‘NEWS_RECEIVED’ must be dispatched. Redux-Saga will take
care about all this.Next let’s go to the main file of this SAGA tutorial.

sagas/index.js

import { put, takeLatest, all } from 'redux-saga/effects';

function* fetchNews() {

const json = yield fetch('https://newsapi.org/v1/articles?


source=
cnn&apiKey=c39a26d9c12f48dba2a5c00e35684ecc')
.then(response => response.json(), );

yield put({ type: "NEWS_RECEIVED", json: json.articles,


});
}

function* actionWatcher() {
yield takeLatest('GET_NEWS', fetchNews)
}

export default function* rootSaga() {


yield all([
actionWatcher(),
]);
}

This file is obviously the most complicated one. I guess it looks quite
challenging because of unusual syntax of ES6 GENERATORS like ‘yield’
and ‘*’. We export from this file function rootSaga in which we call
function actionWatcher.

function* actionWatcher() {
yield takeLatest('GET_NEWS', fetchNews)
}

To put it simple it’s like I’m telling SAGA to wait for action ‘GET_NEWS’
to get dispatched. And ones ‘GET_NEWS’ was dispathced to call
fetchNews function. Inside of fetchNews function happens asynchronous
call to API and when request arrived next action { type:
“NEWS_RECEIVED”, json: json.articles, } is dispatched. As you can see
we don’t even need to write action “NEWS_RECEIVED” in
actions/index.js file because it’s fully described here.

By now let reducer know how to deal with this type of actions
“NEWS_RECEIVED”.

reducers/index.js

const reducer = (state = {}, action) => {


switch (action.type) {
case 'GET_NEWS':
return { ...state, loading: true };
case 'NEWS_RECEIVED':
return { ...state, news: action.json[0], loading:
false }
default:
return state;
}
};
export default reducer;

So this is final code of reducer. Once response from the API call has
received Redux state will have property news which contains json of one
news.

NewsItem.js

import React from 'react';


import { connect } from 'react-redux'
const imgStyle = {
hight: 'auto',
width: '80%',
border: '4px solid RebeccaPurple ',
borderRadius: '5%'
};
const articleStyle = {
width: '50%',
margin: '0 auto',
color: 'olive'
}

let NewsItem = ({ article }) => (


article ?
<article style={articleStyle} >
<div>
<h1>{article.title}</h1>
<img style={imgStyle} src={article.urlToImage} alt="" />
<h4>{article.description}</h4>
<a href={article.url} target="_blank">READ MORE</a>
</div>
</article> :
null
);

const mapStateToProps = (state) => ({


article: state.news,
})

NewsItem = connect(mapStateToProps,null)(NewsItem)
export default NewsItem;

<NewsItem/> component displays the received news.

Loading.js
import React from 'react';
import { connect } from 'react-redux'
import img from '../loading_spinner.gif'

let Loading = ({ loading }) => (


loading ?
<div style={{ textAlign: 'center' }}>
<img src={img} alt='loading' />
<h1>LOADING</h1>
</div> :
null
);

const mapStateToProps = (state) => ({loading:


state.loading})

Loading = connect(mapStateToProps,null)(Loading)

export default Loading;

<Loading/> component displays spinner during async call to API.

You can see the full App code into my repository :

Lavitr/React-Redux-SAGA-tutorial-APP

React-Redux-SAGA-tutorial-APP - Simple React-


Redux-Saga App developed for tutorial article
github.com

Please ‘clap’ if you find this article useful.

S-ar putea să vă placă și