【问题标题】:React Redux fetching data from backend approachReact Redux 从后端方法获取数据
【发布时间】:2020-06-04 02:15:57
【问题描述】:

我有点困惑,希望得到一个能帮助我理清思路的答案。 假设我有一个后端(nodejs、express 等),我在其中存储我的用户及其数据,有时我想从后端获取数据,例如他登录后的用户信息,或者产品列表并保存他们在该州。

到目前为止,我的方法和我所看到的,我在组件加载之前获取数据并使用响应中的数据调度一个操作。 但是我最近开始对此进行一些研究,并且看到了我之前知道的 react-thunk 库,并开始怀疑从后端/API 获取的最佳实践是什么? React Hooks 对这个话题有什么改变吗?知道这一点很重要吗?

我觉得有点笨,但找不到一篇文章或视频准确地谈论这个话题:)

【问题讨论】:

  • 可以在useEffect中获取,使用redux-thunk、redux-saga或者redux-observable。每个选项都有一些优点和缺点,没有明显的赢家。这完全取决于您正在构建的应用程序和您的偏好。我个人更喜欢 redux-saga,因为它很容易测试,而且 api 使用起来很愉快。不过,它与 thunk 非常相似。
  • 感谢您的回答!了解所有这些并尝试它们重要吗?正如你提到的那样,我使用钩子在 useEffect 中进行操作,但这是我知道的唯一方法,我觉得有点落后..
  • 您可以从redux-toolkit.js.org 开始,即使对于复杂的项目也应该足够了。你当然不必知道所有这些。对于简单的应用程序,没有 redux 的钩子就足够了。目前,React 社区甚至有一种趋势,即仅使用上下文和挂钩来构建应用程序。

标签: reactjs redux redux-thunk


【解决方案1】:

要执行此最佳实践,请使用以下方法:

我使用了一些包和模式作为最佳实践:

  • redux-logger 用于记录浏览器控制台中的操作和状态。
  • reselect 选择器可以计算派生数据,允许 Redux 存储可能的最小状态等。
  • redux-thunk Thunks 是基本推荐的中间件 Redux 副作用逻辑,包括复杂的同步逻辑 需要访问存储,以及简单的异步逻辑,如 AJAX 请求 等等。
  • axios 使用 api(基于 Promise 的 HTTP 客户端 浏览器和 node.js)

src 文件夹 中按名称 redux 或您喜欢的任何名称创建目录,然后 然后在redux目录下创建两个文件store.jsrootReducer.js。我们假设从 API 获取产品。

要做到这一点:

在redux目录中创建一个名为product的新目录,然后 在redux/product 目录中创建四个名称为product.types.js, product.actions.js, product.reducer.js, product.selector.js 的文件

项目的结构应该如下

...
src
  App.js
  redux
    product
      product.types.js
      product.actions.js
      product.reducer.js
    rootReducer.js
    store.js
 Index.js
package.json
...

store.js

在这个文件中我们进行 redux 配置

// redux/store.js:
import { createStore, applyMiddleware } from "redux";
import logger from "redux-logger";
import thunk from "redux-thunk";

import rootReducer from "./root-reducer";

const middlewares = [logger, thunk];

export const store = createStore(rootReducer, applyMiddleware(...middlewares));

rootReducer.js

combineReducers 辅助函数会转换一个对象,其值为 您可以将不同的归约函数合并为一个归约函数 传递给createStore

// redux/rootReducer.js
import { combineReducers } from "redux";

import productReducer from "./product/product.reducer";

const rootReducer = combineReducers({
  shop: productReducer,
});

export default rootReducer;

product.types.js 在这个文件中,我们定义了用于管理动作类型的常量。

export const ShopActionTypes = {
  FETCH_PRODUCTS_START: "FETCH_PRODUCTS_START",
  FETCH_PRODUCTS_SUCCESS: "FETCH_PRODUCTS_SUCCESS",
  FETCH_PRODUCTS_FAILURE: "FETCH_PRODUCTS_FAILURE"
};

product.actions.js 在这个文件中,我们为句柄动作创建动作创建者。

// redux/product/product.actions.js
import { ShopActionTypes } from "./product.types";
import axios from "axios";

export const fetchProductsStart = () => ({
  type: ShopActionTypes.FETCH_PRODUCTS_START
});

export const fetchProductsSuccess = products => ({
  type: ShopActionTypes.FETCH_PRODUCTS_SUCCESS,
  payload: products
});

export const fetchProductsFailure = error => ({
  type: ShopActionTypes.FETCH_PRODUCTS_FAILURE,
  payload: error
});

export const fetchProductsStartAsync = () => {
  return dispatch => {
    dispatch(fetchProductsStart());
    axios
      .get(url)
      .then(response => dispatch(fetchProductsSuccess(response.data.data)))
      .catch(error => dispatch(fetchProductsFailure(error)));
  };
};

product.reducer.js 在这个文件中,我们为句柄操作创建了productReducer 函数。

import { ShopActionTypes } from "./product.types";

const INITIAL_STATE = {
  products: [],
  isFetching: false,
  errorMessage: undefined,
};

const productReducer = (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case ShopActionTypes.FETCH_PRODUCTS_START:
      return {
        ...state,
        isFetching: true
      };
    case ShopActionTypes.FETCH_PRODUCTS_SUCCESS:
      return {
        ...state,
        products: action.payload,
        isFetching: false
      };
    case ShopActionTypes.FETCH_PRODUCTS_FAILURE:
      return {
        ...state,
        isFetching: false,
        errorMessage: action.payload
      };
    default:
      return state;
  }
};

export default productReducer;

product.selector.js 在这个文件中,我们从 shop state 中选择 productsisFetching

import { createSelector } from "reselect";

const selectShop = state => state.shop;

export const selectProducts = createSelector(
  [selectShop],
  shop => shop.products
);

export const selectIsProductsFetching = createSelector(
  [selectShop],
  shop => shop.isFetching
);

Index.js 在此文件中,使用 Provider 包装了整个应用程序和组件,以便访问子组件以访问商店和状态。

// src/Index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

import { Provider } from "react-redux";
import { store } from "./redux/store";

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

App.js 类组件 在这个文件中,我们使用类组件连接到存储和状态

// src/App.js
import React, { Component } from "react";

import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
  selectIsProductsFetching,
  selectProducts
} from "./redux/product/product.selectors";

import { fetchProductsStartAsync } from "./redux/product/product.actions";

class App extends Component {
  componentDidMount() {
    const { fetchProductsStartAsync } = this.props;
    fetchProductsStartAsync();
  }

  render() {
    const { products, isProductsFetching } = this.props;
    console.log('products', products);
    console.log('isProductsFetching', isProductsFetching);
    return (
      <div className="App">Please see console in browser</div>
    );
  }
}

const mapStateToProps = createStructuredSelector({
  products: selectProducts,
  isProductsFetching: selectIsProductsFetching,
});

const mapDispatchToProps = dispatch => ({
  fetchProductsStartAsync: () => dispatch(fetchProductsStartAsync())
});

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

或带有功能组件的 App.js(useEffect 挂钩) 在这个文件中,我们使用功能组件连接到存储和状态

// src/App.js
import React, { Component, useEffect } from "react";

import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
  selectIsProductsFetching,
  selectProducts
} from "./redux/product/product.selectors";

import { fetchProductsStartAsync } from "./redux/product/product.actions";

const App = ({ fetchProductsStartAsync, products, isProductsFetching}) => {
  useEffect(() => {
    fetchProductsStartAsync();
  },[]);

    console.log('products', products);
    console.log('isProductsFetching', isProductsFetching);

    return (
      <div className="App">Please see console in browser</div>
    );
}

const mapStateToProps = createStructuredSelector({
  products: selectProducts,
  isProductsFetching: selectIsProductsFetching,
});

const mapDispatchToProps = dispatch => ({
  fetchProductsStartAsync: () => dispatch(fetchProductsStartAsync())
});

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

【讨论】:

  • 谢谢!!最后是一个完整的解释示例,它对我有很大帮助:)
  • 不客气。如果有帮助,请检查投票旁边的绿色标记。祝你好运。
猜你喜欢
  • 1970-01-01
  • 2018-11-07
  • 2021-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
相关资源
最近更新 更多