A Case Against Redux

Redux has been very popular in the React Native community for a long time. As a once in a while React Native developer myself I have come into contact with Redux a lot in recent years and I have some gripes with using Redux in development with React Native today. I’m going to try to outline use cases for and against Redux to explain my thoughts about why Redux is, for the most part, no longer needed for small to medium sized projects.

React Native is a rapidly changing platform that utilizes modern Javascript (ECMAScript) and the feature set for developers is continuously changing. More modern times have seen better ways to integrate applications and website with REST API’s that are baked directly in to the language and toolkits. I see Redux used a lot for fetching and parsing API data to display in applications, so that is what I will talk about in this post specifically. I’m sure there are other uses for Redux that are not covered here but this is intended to be a from experience article rather than a catch-all rant about Redux.

React State

React has a great way to manage state internally. Redux works with this to a certain degree but changes the way you think about state. With Redux you have to write several parts of functionality for it to properly work Actions, Reducers and their required functionality too. All this has to be written instead of directly mapping your data to the built in state management in React. Here’s a look at the differences in data flow between the two platforms in a REST API use case:

Redux

  • Create your Action that calls the endpoint and returns data inside the function
  • Create your Reducer that handles passing data from the Action (with dispatch) to the view
  • Integrate Redux and each individual action into your view with mapStateToProps
  • Take a look at your massive imports now if you have several actions in one view

Example

types.js
1
2
export const CALL_SUCCESS = 'call_success';
export const CALL_FAILED = 'call_failed';

actions.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { 
CALL_SUCCESS,
CALL_FAILED

} from './types';

export const yourAPIAction = (args) => {
return (dispatch, getState) => {
yourApiCall().then((data) => {
//verify data
dispatch({ type: CALL_SUCCESS, payload: data });

}).catch((err) => {
dispatch({ type: CALL_FAILED });
}
};
};
reducer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { 
CALL_SUCCESS,
CALL_FAILED

} from './types';

const INITIAL_STATE = {};

export default(state = INITIAL_STATE, action) => {
switch (action.type) {
case CALL_SUCCESS:
return { ...state, stateData: action.payload };

case CALL_FAILED:
return { ...state, stateData: null };

default:
return { ...state }
}
};
yourComponent.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { yourAPIAction } from './actions';

class yourComponent extends Component {
render() {
return(
<View>
<YourComponent />
</View>
);
}
}

const mapStateToProps = state => {
return {
yourAPIAction: state.yourReducer.yourAPIAction
};
};

export default connect(
mapStateToProps, {
data
});

)(yourComponent);

Non-Redux

  • Create your function for the fetch call. You can do all your functions in your own internal system of files to make future proofing easier if you want.
  • Create a loading indicator or a way to prompt the user that data is being loaded while the fetch call is in transit
  • Handle the return data by mapping it to the state of your component with this.setState({}).

Example

yourComponent.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { yourAPIAction } from './actions';

export default class yourComponent extends Component {
constructor(props) {
super(props);

this.state = {
example: null
};
}

getStuff() {
fetch(stuff).then((data) => {
this.setState({ example: data.example });
// state change auto updates layout components

}).catch((err) => {
// show error
});
}

render() {
return(
<View>
<YourComponent />
</View>
);
}
}

Obviously this is not a complete example, neither example shows how to use the data passed back but you can see that not using Redux signifigantly reduces boilerplate code and makes the flow of data in your project more standard.

Standardization

Javascript is a very open language with a lot of toolkits and libraries that do similar things in vastly different ways. It’s important when working on projects with companies or other developers in general to remember that your code will be worked on by other people who may not specialize in the same libraries you do. Redux creates fragmentation because it heavily modifies the flow of data to the point that it would be difficult for someone without Redux experience to pick up where you left off. Ultimately the biggest case against using libraries like this in small to medium projects is this. However it may be useful to remember that in large applications with a lot of data passed around views libraries like Redux may actually save you and your fellow team members time. Like always you should evaluate your use case and proceed with an understanding of what you are doing and the end goal of the project and it’s structure. Since fetch and state are native parts of Javascript and React respectively it will always be more standard to use them over a large library with loads of boilerplate that completely changes the flow of data through your application.