Migrating a React application from Reflux 
to Redux 
Published: 2020-04-24 11:18:37

In this article I will describe how you can migrate a React application from Reflux to Redux with two example projects. In the first part of the article I will describe how state management looks in Reflux and compare it to the state management in Redux. This kind of migration is useful in larger projects where working with Reflux becomes complicated to maintain, because of React components which start listening to changes coming from multiple stores, which makes the code difficult to understand and to extend 💥. Therefore, migrating to Redux might be a better choice 💪. In the last part of the article I will show the Redux actions and state in the Redux `DevTools` plugin. For the calls to the server I will be using mock server. In order to better understand this article, some basic understanding of React, Reflux and Redux is needed, because the ideas that are presented are relying on basic information about React components, Reflux and Redux state management.  

In Reflux we have three important components: stores, actions and view components. The view components trigger actions, the stores listen to actions and the stores update the view components with new data.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/tkfnn77jpxc3682dwkc3.png)

The state management in Redux is very similar to Reflux, the main difference being the use of reducers.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/61j0g3fdkuowgkp8arwd.png)

I identified the following steps during the migration process.
 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/cwtjlmecrj7pnymi0v8e.png)

#Store creation#

**Reflux**
The `EventsStore` listens to the `loadEventsDetails` action, which is being called by the view component. When the action is called the store is calling an async method which loads the event list from the server and then sets the event information on the state. In order to notify the view components that listen to the store the `trigger` method must be called.
 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/m3n0a7jdruyrlh0ekugu.png)

```js
let state = "EMPTY";
let eventsInformation = [];

const EventsStore = Reflux.createStore({

    init() {
        this.listenTo(EventsActions.loadEventsDetails, this.onLoadEventsDetails);
    },

    async onLoadEventsDetails() {
        try {
            const response = await axios.get('http://localhost:8080/allevents');
            this.updateEventsInformation(response.data);
        } catch (error) {
            this.setError();
        }
    },

    startLoading() {
        state = "LOADING";
        this.trigger();
    },

    updateEventsInformation(events) {
        state = "LOADED";
        eventsInformation = events;
        this.trigger();
    },

    getEventsInformation() {
        return eventsInformation;
    },

    getState() {
        return state;
    },

    setError() {
        state = "ERROR";
        this.trigger();
    },

});
```

**Redux**
Store creation in Redux is very similar to the store creation in Reflux. We will create the store in the `EventsConnector` and since we will use sagas for async calls to the server, what we need to do is to create a saga middleware and connect it to the store and enhance it with the Redux `DevTools` so that we can see in the Google Chrome plugin the evolution of the state. By using the `Provider` from the `react-redux` module we are sending the store to the `AllEvents` container where we are mapping the state to props and the dispatch to props and connect it to the `AllEvents` view component which will be rendered. We need to add all the sagas in the `rootSaga` so that our saga middleware knows about our sagas. Sagas will not be run though, but will run only when we will dispatch an action which will trigger the saga.

```js
export const sagaMiddleware = createSagaMiddleware();
const appliedMiddleware = applyMiddleware(sagaMiddleware);

const enhanceWithDevTools = composeWithDevTools(appliedMiddleware);

const store = createStore(rootReducer, undefined, enhanceWithDevTools);

sagaMiddleware.run(rootSaga);

export const EventsConnector = (props) => (
    <Provider store={store}>
        <AllEvents {...props}/>
    </Provider>
);

export default function* rootSaga(): * {
    yield all([
        fetchAllEvents(),
    ]);
}
```

#Action creation#

**Reflux**
In the actions file I defined one action on which the events store is listening to. The action needs to be created with `Reflux.createActions`. In this file, multiple actions could be added.

```js
const EventsActions: Object = Reflux.createActions({
    loadEventsDetails: {},
});
```

**Redux**
By looking at the actions in Reflux I will create actions in Redux. We need to load the events from the server and for that we will create three actions to do the following: 
-	loading events 
-	finish loading events
-	setting an error 

```js
export const FETCH_EVENTS = "FETCH_EVENTS";
export const FINISH_FETCHING_EVENTS = "FINISH_FETCHING_EVENTS";
export const SET_ERROR = "FINISH_FETCHING_EVENTS";

const createAction = (type, data) => ({ type, data });

export const startFetchingEvents = () => createAction(FETCH_EVENTS);

export const finishFetchingEvents = (events) => createAction(FINISH_FETCHING_EVENTS, events);

export const setError = (error) => createAction(SET_ERROR, error);
```

After each action is dispatched the reducer updates the state in Redux. In the reducer we are defining the initial state. 
-	variable which identifies if we have a loading `LOADING` state while waiting for the server response and or a `LOADED` state 
-	variable which holds information about events 
-	error variable

Everytime the state is updated the components which depend on that state are rerendered.

```js
export const initialState = {
    state: null,
    events: [],
    error: null,
};

export const eventReducer = (state = initialState, action) => {

    switch (action.type) {
        case FETCH_EVENTS:
            return {...state, state: "LOADING"};
        case FINISH_FETCHING_EVENTS:
            return {...state, events: action.data, state: "LOADED"};
        case SET_ERROR:
            return {...state, error: action.data, state: "ERROR"};
    }
    return state;
};

export const rootReducer = combineReducers({
    eventDetails,
});
```

The root reducer is combining multiple reducers, in our case we only have one reducer. The structure of the root reducer is determining the path to the state variables which is used in the container to retrieve the state from Redux.

#API Calls#

**Reflux**
Server calls are done directly from the store.

```js
…
    async onLoadEventsDetails() {
        try {
            const response = await axios.get('http://localhost:8080/allevents');
            this.updateEventsInformation(response.data);
        } catch (error) {
            console.error(error);
            this.setError();
        }
    },
…
```

**Redux**
We will create a saga for each method which does a server call in the Reflux `EventsConnector`. The asynchronous calls will be done with `axios`. 

```js
export function* fetchAllEventsEffect(action): * {
    try {
        const response = yield call(fetchEvents);
        yield put(finishFetchingEvents(response.data));
    } catch (e) {
        yield put(setError(e));
    }
}

export default function* fetchAllEvents(): * {
    yield takeLatest(FETCH_EVENTS, fetchAllEventsEffect);

}

export function fetchEvents() {
    return axios.get('http://localhost:8080/allevents');
}
```

The saga will be called when the `FETCH_EVENTS` event is called. The `AllEventContainer` maps the state in Redux to props and the dispatching of the actions to props and connects it to the React component which gets rendered.

```js
export const mapStateToProps = (state) => ({
    events: state.eventDetails.events,
});

export const mapDispatchToProps = (dispatch) => ({
    fetchAllEvents: () => dispatch(startFetchingEvents()),
});

export default connect(mapStateToProps, mapDispatchToProps)(AllEvents);
```

The response received from our mock server looks like this: 
```js
[
    {"id": 1, "name": "React Beginner Workshop", "description": "Curious about React? Join us for React Essentials."},
    {"id": 2, "name": "Javascript Workshop", "description": "Learning Javascript is always challenging. Challenge yourself at this workshop."},
    {"id": 3, "name": "Java 8 Workshop", "description": "Find out more about Java 8 features."},
    {"id": 3, "name": "Learn Scala", "description": "Introduction in Scala."},
    {"id": 3, "name": "AI Advanced Algorithms", "description": "Let's learn AI together at this fabulous workshop."}
]
```

#Creating the view component#

**Reflux**
The `AllEventsConnector` view component is rendered. The mixins property is used so that the component listens to the data which is sent from the store and then updates the state of the component with the new data. The connection to the store is done using `Reflux.listenTo` having as parameters the store and the method which is called when new data from the store is sent to the component. In the lifecycle method `componentDidMount` the Reflux action `loadEventsDetails` is called. The events information is then sent via props to the `AllEvents` component to be displayed.

```js
const AllEventsConnector = createClass({

    mixins: [
        Reflux.listenTo(EventsStore, "handleStoreUpdate"),
    ],

    getInitialState() {
        return this.getCurrentState();
    },

    handleStoreUpdate() {
        this.setState(this.getCurrentState());
    },

    getCurrentState() {
        return {
            eventsInformation: EventsStore.getEventsInformation(),
        };
    },

    componentDidMount() {
        EventsActions.loadEventsDetails();
    },

    render() {
        return (
            <AllEvents eventsInformation={this.state.eventsInformation}/>
        );
    }

});
```

**Redux**
The `AllEvents` component is using hooks to call `fetchAllEvents` and dispatch the action from the container and then when the `props.events` is updated the component will be re-rendered and the information about the events will get displayed.

```js
const AllEvents = (props) => {

    useEffect(() => {props.fetchAllEvents()}, [props.fetchAllEvents]);

    return props.events.map(event => <div key={event.id}> {event.name} </div>)
};
```

#Redux Dev Tools#

An advantage of using Redux is the fact that you can use the Redux `DevTools` plugin in Google Chrome and see the state and the actions triggered.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/zuuaamh9yn4k2mw8yasx.png) 

#Cloning the projects

If you want to see the code for both projects you can clone the repositories from the links below. In order to start the projects please check the steps in the Readme files 🖥.

[Sample Reflux project can be found here](https://github.com/kittyradulescu/reflux-example)

[Sample Redux project can be found here](https://github.com/kittyradulescu/redux-example)

After starting the project on http://localhost:3000/ you will get the events displayed.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/78jwndce6hyrigno2cs4.png)

I hope this article described how you can migrate an older project using Reflux to Redux and helps you migrate. Please let me know in the comments section below if you have other suggestions for migrating a Reflux store to Redux and which were the problems that you encountered. 🛠 

Thanks 🙏