Codementor Events

Redux, Reactotron, Actions, Reducers and Sagas (2)

Published Jun 02, 2018Last updated Nov 29, 2018
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

Screen Shot 2018-06-03 at 12.02.52 AM.png

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

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.

Screen Shot 2018-06-02 at 11.52.58 PM.png

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:

Discover and read more posts from Kuldeep
get started