简体   繁体   中英

React Functional Component, Redux and Loading Data

I am currently trying to pick up functional react and am having a tough time figuring out the appropriate way to load data from an API on page load. I am using react, redux and thunks. I have modified the project that i created from this course . My code currently looks like this

store.js

import {createStore, applyMiddleware, compose} from "redux";
import rootReducer from "./reducers";
import reduxImmutableStateInvariant from 'redux-immutable-state-invariant';
import thunk from "redux-thunk";


export default function configureStoreDev(initialState){

    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; // add support for redux dev tools

    return createStore(
        rootReducer,
        initialState,
        composeEnhancers(applyMiddleware(thunk, reduxImmutableStateInvariant()))
    );
}

authorApi.js

import { handleResponse, handleError } from "./apiUtils";
const baseUrl = process.env.API_URL + "/authors/";

export function getAuthors() {
  return fetch(baseUrl)
    .then(handleResponse)
    .catch(handleError);
}

authorActions.js

import * as actionTypes from "./actionTypes";
import * as authorApi from "../../api/authorApi";
import {apiCallError, beginApiCall} from "./apiStatusAction";


export function loadAuthorsSuccess(authors){
    return {type: actionTypes.LOAD_AUTHORS_SUCCESS, authors: authors};
}

export function loadAuthors(){
    return function(dispatch){

        // invoke loader
        dispatch(beginApiCall());

        return authorApi.getAuthors().then(response => {
            dispatch(loadAuthorsSuccess(response.authors));
        }).catch(error => {
            dispatch(apiCallError());
            throw error;
        })
    }
}

authorReducer.js

import * as actionTypes from "../actions/actionTypes";
import initialState from "./initialState";

export default function authorReducer(state = initialState.authors, action){
    switch(action.type){
        case actionTypes.LOAD_AUTHORS_SUCCESS:
            return action.authors;
        default:
            return state;
    }
}

AuthorsPage.js

import React, {useState, useEffect} from 'react';
import {connect} from 'react-redux';
import AuthorsList from "./AuthorsList";
import Spinner from "../common/Spinner";

import {loadAuthors} from "../../redux/actions/authorActions";
import {loadCourses} from "../../redux/actions/courseActions";

function AuthorsPage({loadAuthors, deleteAuthor, loadCourses, ...props}) {
    const [authors, setAuthors] = useState([...props.authors]);
    const [courses, setCourses] = useState([...props.courses]);
    const [loading, setLoading] = useState(props.loading);
    const [redirectToAddAuthorPage, setRedirectToAddAuthorPage] = useState(false);

    useEffect(() => {
        loadAuthors().catch(error => {
            console.log("Loading authors failed: " + error);
        });
        loadCourses().catch(error => {
            console.log("Loading courses failed: " + error);
        });
    }, []);

    useEffect(() => {
        if(authors !== props.authors && props.authors.length > 0){
            setAuthors([...props.authors]);
        }
    }, [props.authors]);

    useEffect(() => {
        setLoading(props.loading)
    }, [props.loading]);



    return (
        <>
            <h2>Authors</h2>
            {loading === true ? <Spinner/> : (
                <>
                    <button
                        style={{ marginBottom: 20 }}
                        className="btn btn-primary add-course"
                        onClick={() => setRedirectToAddAuthorPage(true )}
                    >
                        Add Author
                    </button>
                    <AuthorsList authors={authors}  />
                </>
            )}

        </>
    );
}

function mapStateToProps(state){
    return {
        authors: state.authors,
        courses: state.courses,
        loading: state.apiCallsInProgress > 0

    }
}
const mapDispatchToProps = {
    loadAuthors: loadAuthors,
    loadCourses: loadCourses
}

export default connect(mapStateToProps, mapDispatchToProps)(AuthorsPage);

The part that i am having trouble with is getting the state for the authors list to set appropriately. I tried setting it after the call to loadAuthors() but am not getting the authors list returned in the .then() of that call.

The next option was to listen for the author props to change with useEffect but this doesn't seem to be the most efficient or correct way of loading the data that i need on the initial load.

I cant seem to find any articles or tutorials detailing the appropriate method to accomplish this from a functional component perspective and would appreciate any guidance or pointers in the right direction.

The next option was to listen for the author props to change with useEffect but this doesn't seem to be the most efficient or correct way of loading the data that i need on the initial load.

You could render props.authors directly without assigning it to useState

Example

 const thunk = ReduxThunk.default; const { connect, Provider } = ReactRedux; const { bindActionCreators, combineReducers, createStore, applyMiddleware } = Redux; window.redux = Redux; const initialState = { authors: [] }; const getAuthors = () => Promise.resolve(Array(10).fill(0).map((pr, index) => ({id: index, name: `Author ${index + 1}`}))) const loadAuthorsSuccess = (authors) => { return {type: 'LOAD_AUTHORS_SUCCESS', authors}; } const loadAuthors = () => { return function(dispatch) { return getAuthors().then(authors => { dispatch(loadAuthorsSuccess(authors)); }) } } const authorReducer = (state = initialState.authors, action) => { switch(action.type) { case 'LOAD_AUTHORS_SUCCESS': return action.authors; default: return state; } } const reducer = combineReducers({ authors: authorReducer}) const store = createStore( reducer, initialState, applyMiddleware(thunk) ); const { useState, useEffect } = React; function mapStateToProps({authors}) { return { authors } } const mapDispatchToProps = { loadAuthors } const _AuthorsPage = ({loadAuthors, authors}) => { useEffect(() => { loadAuthors().catch(error => { console.log("Loading authors failed: " + error); }); }, []) return <div> {authors.map(({id, name}) => <div key={id}>{name}</div>)} </div> } const AuthorsPage = connect(mapStateToProps, mapDispatchToProps)(_AuthorsPage) const App = () => { return <div> <AuthorsPage/> </div> } ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') );
 <script src="https://unpkg.com/react/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <script src="https://unpkg.com/redux@4.0.5/dist/redux.js"></script> <script src="https://unpkg.com/react-redux@latest/dist/react-redux.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/redux-thunk/2.3.0/redux-thunk.js"></script> <div id="root"></div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM