Writing our first Redux parts

Our First Action

Let’s write our first action! We’ll start with the location field, since it’s a very typical example. An action function in Redux returns an object with a type and can optionally also pass some data along the way. Our changeLocation action looks like this:

function changeLocation(location) {
  return {
    type: 'CHANGE_LOCATION',
    location: location
  };
}

This action thus has a type of 'CHANGE_LOCATION' and passes along some data with the location property.

That’s nice and all, but this won’t change the state automatically. We have to tell Redux what to do when this action comes in, which we do in a so-called reducer.

A reducer is a simple function that takes two arguments, the current state and the action that was dispatched:

function mainReducer(state, action) {
  return state;
}

Right now, no matter what action comes in and what data it has the state will always stay the same – that’s not quite optimal, as nobody will be able to work with the app! Let’s change the location field in the state based on the data in the action with the 'CHANGE_LOCATION' type.

function mainReducer(state, action) {
  switch (action.type) {
    case 'CHANGE_LOCATION':
      state.location = action.location;
      return state;
  }
}

What we’re doing here is mutating the state. We assign state.location the value of action.location. This is discouraged by Redux because it introduces potential bugs and side effects. What we instead should be doing is returning a new object which is a copy of the state!

JavaScript has a handy function called Object.assign, which allows you to do that. Let’s take a look at the solution first:

function mainReducer(state, action) {
  switch (action.type) {
    case 'CHANGE_LOCATION':
      return Object.assign({}, state, {
        location: action.location
      });
  }
}

By passing in a new, empty object ({}) as the first argument and the current state as the second one, we create a carbon copy of the state. The third argument of the function ({ location: action.location }) is just the changes to our state!

This creates a new object, meaning the state stays the same which is A+ behaviour and will keep us from a lot of bugs!

With a bit of glue this’ll already work! We should do two more small things to make this better: we should return the state unchanged if no action we want to handle comes in and we should use the initial state if state is undefined:

var initialState = {
  location: '',
  data: {},
  dates: [],
  temps: [],
  selected: {
    date: '',
    temp: null
  }
};

function mainReducer(state = initialState, action) {
  switch (action.type) {
    case 'CHANGE_LOCATION':
      return Object.assign({}, state, {
        location: action.location
      });
    default:
      return state;
  }
}

We’ll now need to dispatch this action when the location changes:

class App extends React.Component {
 fetchData = (evt) => { /* … */ };
 onPlotClick = (data) =>  { /* … */ };
 changeLocation = (evt) => {
   this.props.dispatch(changeLocation(evt.target.value));
 };
 render() { /* … */ }
});

Don’t worry about where this.props.dispatch comes from for now, we’ll get to that!

Imagine evt.target.value is "Sydney, Australia", this is what our global state is going to look like when we dispatch the changeLocation action:

{
  location: 'Sydney, Australia',
  /* …the rest stays the same… */
}

Tying it all together

Now that we understand the basic parts that are involved, let’s tie it all together!

First, we need to create a store for our state and provide the state to our root App component. The store combines all of the apps reducers and (as the name suggests) stores the state. Once the store is set up though, you can forget about it again since we’ll be using the state, but not the store directly!

Redux allows us to create a single Store for the whole App

We do this in our main index.js file, and we’ll use the createStore function from the redux package and the Provider component from the react-redux package.

First, import those functions:

// index.js

/* … */
import ReactDOM from 'react-dom';

import { createStore } from 'redux';

import { Provider } from 'react-redux';

import App from './App.js';
/* … */

Then we need to create our store:

// index.js

/* … */
import App from './App.js';

var store = createStore();

ReactDOM.render(
/* … */
);

Lastly, we need to wrap our App component in the Provider and pass in the store:

Lastly, we need to wrap our App component in the Provider and pass in the store:

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

And that’s it, our Redux integration is done! 🎉. Let’s look at this in action

Get hands-on with 1200+ tech skills courses.