Getting Started with React and Redux: Lesson 8 of 10

Displaying a Character's World

Up Next

Displaying a Character's Movies

Autoplaying in 7 seconds!

Cancel

Now, let's add the ability to see the character's world when a character is clicked on. This should be pretty simple. Add an action to get the data and set the data. Add a reducer to put it into state. Lastly, create view that gets the data from state and displays it on screen.

Let's create an action that will take in a URL that is an endpoint to get the world's data, hits that endpoint, and then dispatches a new action to set the data. Add the following to src/reducer/character/actions.js.

export function getCharacterWorld(url) {
  return dispatch =>
    fetch(url)
      .then(res => res.json())
      .then(world =>
        dispatch(setCharacterWorld(world))
      );
}

This should look familiar. We are returning a thunk that hits the endpoint we pass in, gets the world data from the response, and then dispatches a new action created using a new action creator when passed the world data. Let's add the setCharacterWorld action creator to this same file.

export const SET_CHARACTER_WORLD = 'SET_CHARACTER_WORLD';

export function setCharacterWorld(world) {
  return {
    type: SET_CHARACTER_WORLD,
    world,
  };
}

This will handle dispatching the correct data to the store, but where are we supposed to get this magical world URL from and where are should we dispatch this action from. We can get this data from the character's profile that is returned from the API. Therefore, we can kick this action off from inside the callback inside our getCharacterProfile action creator. Add the following line to the inside of that callback. Just below the dispatching of the setCharacterProfile action creator call.

dispatch(getCharacterWorld(profile.homeworld));

Now, when the call to the profile endpoint comes back, we will grab the world from this data and dispatch our new action to get the world data and set it in state.

Let's create a reducer to put this world data into state. First, make your reducer inside src/reducer/character/index.js look like the below piece of code.

import { combineReducers } from 'redux';
import id from './id';
import profile from './profile';
import world from './world';

export default combineReducers({
  id,
  profile,
  world,
});

Next, we need to actually create the reducer we added here. Create a file at src/reducer/character/world.js and put the following in it.

import { SET_CHARACTER_WORLD } from './actions';

const initialState = {};

export default (state = initialState, action) => {
  switch (action.type) {
    case SET_CHARACTER_WORLD:
      return action.world;
    default:
      return state;
  }
};

This is much like our other reducers. We are just looking for a specific action to come through, and when it does, we pull the world data out of it and put it into our state.

Now that our data will be in our state, we just need to display it. Make your src/components/App.js look like below by bringing in a CharacterWorld and displaying it in our App.

import React from 'react';
import CharacterList from './CharacterList';
import CharacterProfile from './CharacterProfile';
import CharacterWorld from './CharacterWorld';

const App = () =>
  <div className='container'>
    <div className='row'>
      <CharacterList />
      <CharacterProfile />
    </div>
    <div className='row'>
      <CharacterWorld />
    </div>
  </div>;

export default App;

Now, create a file at src/components/CharacterWorld.js and put the below code in it.

import React from 'react';
import { connect } from 'react-redux';

const isKnown = text => text && text !== 'unknown';

const CharacterWorld = ({ world }) =>
  <div id='character-world' className='col-md-6'>
    <h1>World</h1>
    {isKnown(world.name) && <p>Name: {world.name}</p>}
    {isKnown(world.population) && <p>Population: {world.population}</p>}
    {isKnown(world.diameter) && <p>Diameter: {world.diameter}km</p>}
    {isKnown(world.rotation_period) && <p>Day length: {world.rotation_period} hours</p>}
    {isKnown(world.orbital_period) && <p>Year length: {world.orbital_period} days</p>}
    {isKnown(world.climate) && <p>Climate: {world.climate}</p>}
  </div>;

const mapStateToProps = ({ character: { world } }) => ({
  world,
});

export default connect(mapStateToProps)(CharacterWorld);

Again, we have used mapStateToProps to pull the world out of our state and supply it to our component through props. In our component, we pull the world out of props and then proceed to display different parts of it. Some of the data could come back blank or as the string unknown. Therefore, I have created a helper function that makes sure the data is there and not equal to unknown. We can use this function as a guard to make sure we only show data that is populated.

To make this a little more fun, we can also show the name of the world in our profile data. In src/components/CharacterProfile.js, we can pull the world data out of state in mapStateToProps.

const mapStateToProps = ({ character: { profile, world } }) => ({
  profile,
  world,
});

Then, we can pull that out props to use it in our component.

const CharacterProfile = ({ profile, world }) =>

Lastly, we display it in our component.

{world.name && <p>Homeworld: {world.name}</p>}

Notice how because everything is in one place, we can pull out anything we need whenever we need it.

We have one more piece to display. We want to show all the movies the character is in. This process is very similar to displaying the character's world. See if you can figure it out yourself before watching the next video!

Chris Sevilleja

151 posts

Co-founder of Scotch.io. Slapping the keyboard until something good happens.