DEV Community

Ting Chung
Ting Chung

Posted on

Making fetch requests to Redux thunk

React is such a powerful Javascript library! In my time using it, the advantages of using it over plain vanilla Javascript are amazing. Being able to actively render, store and manipulate state, and pass down props is so critical in making an interactive experience on the front-end. With that said, there are so many tools that enhance the web development experience but if you want to design a website that gives you centralized state for a large web application, you need Redux. Dan Abramov goes into detail in this youtube video below.

I love being able to time-travel in your state with the Redux web development toolkit. It really helps the developer fully understand where anything broke down, what are the actions passed, what was the payload. But with an application that uses Redux, we really need to use the middleware thunk to specifically help with AJAX fetch requests. Typically, when we are performing our fetch requests, our result needs to be fed into our Redux store. Let's dive into some code, shall we?

export function fetchLogin(data) {
    fetch('http://localhost:3000/api/v1/login', {

            credentials: "include",
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify({
                user: {
                    email_address: data.email,
                    password: data.password

                }
            })
        })
       return {
            type: 'GET_CURRENT_USER', payload: {
                user: {
                    userId,
                    bio,
                    name
                }
            }

        }
}
Enter fullscreen mode Exit fullscreen mode

In this code above, I am performing a fetch to login a user via email and password. What I want afterwards is the user info. Now the problem with doing this is that fetch has Promises. If your promise is returned later than when you call "return", you will have no returned data. The function has no way to dispatch to Redux the desired action to update the Redux store. Let's This is where Redux-Thunk steps in. Here is what the top level index.js would look like. This is very basic. We are only calling on "applyMiddleware" on thunk. Make sure to perform 'npm install redux-thunk'.

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const store = createStore(rootReducer, applyMiddleware(thunk));

ReactDOM.render(
  <Provider store={store} >
    <App />
  </Provider>, document.getElementById('root')
)
Enter fullscreen mode Exit fullscreen mode

What we need is dispatch to be passed in so that we can use it. In my LoginContainer component, I need to pass in dispatch to props. Here is what the whole component looks like:

import React, { Component } from 'react'
import { Form, Row, Col, Button } from 'react-bootstrap'
import { connect } from 'react-redux'
import { fetchLogin } from '../actions/fetchLogin'


class LoginContainer extends Component {
    constructor (props) {
        super(props)
        this.state = {
            email: "",
            password: ""
        }

        this.handleChange = this.handleChange.bind(this)
        this.handleSubmit = this.handleSubmit.bind(this)
    }

    handleChange = (event) => {
        this.setState(previousState => {
            return {
                ...previousState,
                [event.target.name]: event.target.value
            }
        })

    }

    handleSubmit = (event) => {
        event.preventDefault()

        this.props.fetchLogin({
            email: this.state.email,
            password: this.state.password
        },this.props.history)

    }

    render () {
        return (
            <React.Fragment>
                <Form onSubmit={this.handleSubmit} >
                    <Form.Group as={Row} controlId="formHorizontalEmail">
                        <Form.Label column sm={2}>
                        Email
                        </Form.Label>
                        <Col sm={10}>
                        <Form.Control as="input" name="email" type="email" placeholder="Email" onChange={this.handleChange} />
                        </Col>
                    </Form.Group>

                    <Form.Group as={Row} controlId="formHorizontalPassword">
                        <Form.Label column sm={2}>
                        Password
                        </Form.Label>
                        <Col sm={10}>
                        <Form.Control as="input" name="password" type="password" placeholder="Password" onChange={this.handleChange} />
                        </Col>
                    </Form.Group>
                        <Form.Group as={Row}>
                            <Col sm={{ span: 10, offset: 2 }}>
                            <Button type="submit">Sign in</Button>
                            </Col>
                    </Form.Group>
                </Form>
            </React.Fragment>
        )
    }
}

function mapDispatchToProps(dispatch){
    return { fetchLogin: (data, history) => dispatch(fetchLogin(data, history)) }
  }

  function mapStateToProps(state){
    return {
        loggedIn: !!state.currentUser,
        router: state.router,
        entries: state.entries.entries,
        currentUser: state.currentUser
    }
  }

export default connect(mapStateToProps, mapDispatchToProps)(LoginContainer)
Enter fullscreen mode Exit fullscreen mode

The main takeaway is my mapDispatchToProps function that feeds in dispatch to fetchLogin. Then fetchLogin will invoke dispatch to perform Redux actions.

Here is what our fetchLogin looks like now that we've implemented Redux-Thunk:

export function fetchLogin(data, history) {
    return (dispatch) => {
        dispatch({type: 'LOGIN_REQUEST_STARTED'})

        return fetch('http://localhost:3000/api/v1/login', {

            credentials: "include",
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify({
                user: {
                    email_address: data.email,
                    password: data.password

                }
            })
        })
        .then(resp => resp.json())
        .then(userData => {

            dispatch({type: 'SET_CURRENT_USER', userData})
            history.push("/entries")

        })
    }
}
Enter fullscreen mode Exit fullscreen mode

What we're doing here is part of the Redux-Thunk middleware magic. We are able to return a function that passes in 'dispatch' because we need to use it later to make updates to our Redux store. After getting the userData, we can invoke 'dispatch' to set the current user. After that, I redirect the user to my '/entries' route. A full justification to use Redux-Thunk can be found here:

GitHub logo reduxjs / redux-thunk

Thunk middleware for Redux

Redux Thunk

Thunk middleware for Redux. It allows writing functions with logic inside that can interact with a Redux store's dispatch and getState methods.

For complete usage instructions and useful patterns, see the Redux docs Writing Logic with Thunks page.

GitHub Workflow Status npm version npm downloads

Installation and Setup

Redux Toolkit

If you're using our official Redux Toolkit package as recommended, there's nothing to install - RTK's configureStore API already adds the thunk middleware by default:

import { configureStore } from '@reduxjs/toolkit'

import todosReducer from './features/todos/todosSlice'
import filtersReducer from './features/filters/filtersSlice'

const store = configureStore({
  reducer: {
    todos: todosReducer,
    filters: filtersReducer,
  },
})

// The thunk middleware was automatically added
Enter fullscreen mode Exit fullscreen mode

Manual Setup

If you're using the basic Redux createStore API and need to set this up manually, first add the redux-thunk package:

npm install redux-thunk

yarn add redux-thunk
Enter fullscreen mode Exit fullscreen mode

The thunk middleware is a named export.

More Details:

Happy coding!

Top comments (0)