DEV Community

Lestley Gabo
Lestley Gabo

Posted on • Updated on

How to make a user friendly select when using React, Redux, and Firebase (Part 2) - Get real data

This will be impossible to follow if you don't have Redux set up.

I use data from CDragon. They parse and serve League of Legends (LoL) assets into APIs. They are almost always up to date and have an active community and is probably the easiest way to get LoL assets.

How I call the CDragon API:

  • I have a component <InitData />, and it's called inside App.js.
  • I keep it simple and bypass CORS issues by using a proxy. I am sure using fetch you can skip the proxy but I don't want to fix what isn't broken.
// InitData.js
import React from 'react';
import axios from 'axios';
import { connect } from 'react-redux';

import { getDataCDragon } from '../../store/actions/GetData';
const PROXY_URL = 'https://cors-anywhere.herokuapp.com/';
const API_URL_CDRAGON = 'https://raw.communitydragon.org/latest/cdragon/tft/en_us.json';
  • I then parse the data inside componentDidMount(){} to make sure the data is taken before the render.
// InitData.js
 componentDidMount() {
        let cDragonData = axios({
            method: 'get',
            url: PROXY_URL + API_URL_CDRAGON,
            responseType: 'json',
            crossdomain: true
        });

        Promise.all([cDragonData])
            .then(values => {
                let itemsData = values[0].data.items;

           // filter out hex items
                itemsData = itemsData.filter(itemData => itemData.id > 0 && itemData.id < 9000);

                tftData = {
                    itemsData: itemsData,
                };

                // calls action getData
                this.props.getDataCDragon(tftData);
            })
            .catch(err => {
                console.log('fetch data error', err);
                this.setState({ isFailed: true });
            });
    }
  • Used axios to promise the data. Did a lot of console logging to see what kind of data I was getting, i.e. objects vs arrays.
  • I am familiar with the JSON data from the CDragon API and so I know how to filter the items data. (-) IDs are old or unused, (> 9000) IDs are hexes.
  • The important part is itemData being packaged into an object. (It's funny looking, but keep in mind I parsed more than just itemsData)
  • this.props.getDataCDragon(tftData); is an action that will save itemsData into the Redux store (just store from now on).
// InitData.js
const mapDispatchToProps = dispatch => {
    return {
        getDataCDragon: tftData => dispatch(getDataCDragon(tftData))
    };
};

export default connect(null, mapDispatchToProps)(InitData);
  • Inside action GetData.js.
  • An action can easily be put inside the component where it's used. However, it is best practice to separate the actions from the components. The code will be cleaner and easier to understand.
// GetData.js
export const getDataCDragon = tftData => {
    return (dispatch, getState) => {
        // make an async call to database
        dispatch({ type: 'GET_DATA_CDRAGON', tftData });
    };
};
  • Inside reducer MixedReducer.js.
  • I named it MixedReducer because it used to be my rootReducer until I added more reducers so its name had to adapt.
  • ...state, is very important because we don't want to overwrite the state (all the data) inside the store
  • Just keep names consistent, don't bother confusing yourself with different names in different files.
// MixedReducer.js
// **********************************************
// **** Using Redux Checklist ****
// **********************************************
/*
    1.) connect component to store
    2.) map state to props (get data) or map dispatch to props (set data)
    3.) in the component, use maapped data with this.props
    4.) add the mapped const into the connect HOC - order goes connect(mapStateToProps, mapDispatchToProps) null if empty
    5.) the mapped const calls an action (separation of concerns) -> make action, e.g. UPDATE_DATA
    6.) reducer will catch this action -> create switch case for that corresponding action 
*/

const initState = {
    itemsData: [],
};

const mixedReducer = (state = initState, action) => {
    switch (action.type) {
        case 'GET_DATA_CDRAGON':
            return {
                ...state,
                itemsData: action.tftData.itemsData,
            };
        default:
            return state;
    }
};

export default mixedReducer;

Why do all of this? Why even use Redux? What's the point of saving the data to the store?

  • Since <InitData /> was called at App.js, we can use the parsed data by passing it through props to its children. The first iteration of the app worked like that, but it was too tedious passing data around with props. If you suddenly need to add new data on a child component you have to props pass that data all the way from parent to child, parent to child, parent to child.
  • The best practice for connecting a component to the store is to use the connect HOC (higher order component), i.e. export default connect(null, mapDispatchToProps)(InitData);.
  • The component being connected to the store means it can watch (or read) any data from the store via mapStateToProps which would have been where null was in our code --> export default connect(null, mapDispatchToProps)(InitData);.
  • Read from store --> mapStateToProps.
  • Write to store --> mapDispatchToProps.
  • In conclusion, we are using Redux to write our data to the store. Allowing us to easily read it later on inside other components.

**Update**

I didn't use select at all. Current progress shown in Gif below. Back-end completed. Because it is working, I can now continue the documentation of current build.
update- backend works

Top comments (0)