Redux, Reactotron, Actions, Reducers and Sagas (2)
A continuation of my old post, Please make sure that you have gone through the previous post before getting started here. Redux, Store, Actions, Reducers and logger: Get Started and a little further. This post will help you get started with redux-saga
and reactotron
inside a react-native
app.
Content
Get Started
You can either follow this post OR directly get the final code from my github repo. here.
Let's start by uninstalling the packages. (Ignore if you ignored the previous post 😛):
Uninstall redux-thunk
and redux-logger
(Removing the logger as reactatoron will log the saga effects as well.)
Now let's install saga and reactotron helpers.
-
npm install --save redux-saga
-
npm install --save-dev reactotron-apisauce reactotron-react-native reactotron-redux-saga
How does it work?
- User types something.
- onTextChange calls the action.
- Action dispatched to store.
- Pulled by a watcher saga.
- Watcher calls worker.
- Worker does the logic part(if any) and later calls an effect(put).
- new object with type and payload sent to all reducers.
- reducers calculates new app state.
- state sent to all components.
- component rerender with new props(using connect helper from react-redux).
- wait for new change.
- repeat.
Structure
- index.js
- src/
- Components/
- CustomTextInput.js
- redux/
- actions/
- reducers/
- sagas/
- store/
- types.js
- App.js # Entry point
...
Redux
Update types
types.js
export const TEXT_CHANGED = 'text_changed';
export const TEXT_CHANGED_SUCCESS = 'text_changed_success';
Actions
index.js
export * from './CustomTextInputActions';
CustomTextInputActions.js
import { TEXT_CHANGED } from '../types';
export const textChanged = text => {
return {
type: TEXT_CHANGED,
payload: text
};
};
Sagas
- What is redux-saga? ReadMe
- Effects: DeclarativeEffects
- Watcher/Worker: #Watcher/Worker
index.js
import { all, fork } from 'redux-saga/effects';
import CustomTextInputSagas from './CustomTextInputSagas';
export default function* rootSaga() {
yield all([
fork(CustomTextInputSagas.watcherTextChanged),
]);
}
CustomTextInputSagas.js
Watchers are the sagas who watch for any action that is dispatched to the store and then they delegate an action to a worker saga.
- Watcher: Will watch for dispatched actions and fork a worker on every action
- Workers: Process the code (business logic/ API calls) or even call other workers, handle the action and terminate
import { put, take, fork } from 'redux-saga/effects';
import { TEXT_CHANGED, TEXT_CHANGED_SUCCESS } from '../types';
// ****************
// WORKERS
// ****************
function* workerTextChanged(action) {
console.log(action);
// {type: "text_changed", payload: "-_-"}
try {
yield put({ type: TEXT_CHANGED_SUCCESS, text: action.payload });
} catch (e) {
console.log('Error', e);
}
}
// ****************
// WATCHERS
// ****************
export function* watcherTextChanged() {
while (true) {
const action = yield take(TEXT_CHANGED);
yield fork(workerTextChanged, action);
}
}
Configurations
Make the configuration file for reactotron
Reactotron will display the logs and calls for Saga effects
, AsyncStorage
and API calls
.
configs.js
import Reactotron from 'reactotron-react-native';
import sagaPlugin from 'reactotron-redux-saga';
import apisaucePlugin from 'reactotron-apisauce';
Reactotron.configure({ name: 'GiftCard' })
.useReactNative()
.use(apisaucePlugin())
.use(sagaPlugin());
Reactotron.connect(); // Connect with reactotron
Reactotron.clear(); // Clear the logs.
const sagaMonitor = Reactotron.createSagaMonitor();
export { sagaMonitor };
Store
Since we are using Saga
now, we need to update the store.
index.js
import { createStore, compose, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import { sagaMonitor } from '../../configs';
import reducers from '../reducers'; // Our Reducers
import rootSaga from '../sagas'; // Our Sagas
const middleWare = [];
// Setup Redux-Saga
const sagaMiddleware = createSagaMiddleware({ sagaMonitor });
middleWare.push(sagaMiddleware);
const store = createStore(reducers, {}, compose(applyMiddleware(...middleWare)));
// Initiate the root saga.
sagaMiddleware.run(rootSaga);
export default store;
Component
CustomTextInput.js
import React, { Component } from 'react';
import { AppRegistry, TextInput, View } from 'react-native';
import { connect } from 'react-redux';
import * as actions from '../redux/actions';
class CustomTextInput extends Component {
onTextChange = text => {
this.props.textChanged(text);
};
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<TextInput
onChangeText={text => this.onTextChange(text)}
placeholder="Search"
value={this.props.text}
style={{ height: 80, width: 200, fontSize: 30 }}
/>
</View>
);
}
}
const mapStateToProps = state => {
return {
text: state.TextReducer.text
};
};
export default connect(mapStateToProps, actions)(CustomTextInput);
Conclusion
Thank you for reading this post — I hope you found this helpful. You can find me on GitHub, LinkedIn and CodeMentor. If you have any questions, feel free to reach out to me!
More posts: