React 功能组件、Redux 和加载数据

React Functional Component, Redux and Loading Data

我目前正在尝试学习功能性反应,并且很难找到在页面加载时从 API 加载数据的适当方法。我正在使用 react、redux 和 thunks。我已经修改了我从 this course 创建的项目。我的代码目前看起来像这样

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);

我遇到问题的部分是正确设置作者列表的状态。我尝试在调用 loadAuthors() 后设置它,但没有在该调用的 .then() 中返回作者列表。

下一个选项是监听作者道具随 useEffect 的变化,但这似乎不是我在初始加载时加载所需数据的最有效或正确的方式。

我似乎找不到任何文章或教程详细说明从功能组件的角度完成此操作的适当方法,我将不胜感激任何正确方向的指导或指示。

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.

您可以直接渲染 props.authors 而无需将其分配给 useState

例子

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>