React Redux 从后端方法获取数据
React Redux fetching data from backend approach
我有点困惑,希望得到一个能帮助我理清思绪的答案。
假设我有一个后端(nodejs、express 等),我在其中存储我的用户和他们的数据,有时我想从后端获取数据,比如他登录后的用户信息,或者产品列表并保存他们在州内。
到目前为止我的方法和我所看到的,我在组件加载之前获取数据并使用响应中的数据分派操作。
但是我最近开始深入研究这个问题,我看到了我早先知道的 react-thunk 库,并开始怀疑从 backend/API 中获取数据的最佳实践是什么?
React Hooks 是否改变了这个主题?知道这些很重要吗?
我觉得有点笨但是找不到一篇文章或视频来准确讨论这个话题:)
要执行此最佳实践,请使用以下方法:
我使用了一些包和模式作为最佳实践:
- redux-logger 用于在浏览器控制台中记录操作和状态。
- reselect 选择器可以计算派生数据,允许 Redux
存储最小可能状态等
- redux-thunk Thunks是推荐的basic中间件
Redux 副作用逻辑,包括复杂的同步逻辑
需要访问商店,以及像 AJAX 请求这样的简单异步逻辑
等等
- axios 与 api 一起工作(基于 Promise 的 HTTP 客户端
浏览器和 node.js)
create a directory by name redux or any name of you like in src folder and
then create two files store.js
and rootReducer.js
in redux directory. We assume fetch products from API.
要做到这一点:
Create a new directory by name product in redux directory and then
create four files by names product.types.js, product.actions.js,
product.reducer.js, product.selector.js
in redux/product
directory
项目结构应该如下
...
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
The combineReducers
helper function turns an object whose values are
different reducing functions into a single reducing function you can
pass to 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
在此文件中,我们 select products
和 isFetching
来自商店状态。
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 class分量
在此文件中,我们使用 class 组件
连接到商店和状态
// 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 hook)
在这个文件中,我们使用功能组件连接到商店和状态
// 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);
我有点困惑,希望得到一个能帮助我理清思绪的答案。 假设我有一个后端(nodejs、express 等),我在其中存储我的用户和他们的数据,有时我想从后端获取数据,比如他登录后的用户信息,或者产品列表并保存他们在州内。
到目前为止我的方法和我所看到的,我在组件加载之前获取数据并使用响应中的数据分派操作。 但是我最近开始深入研究这个问题,我看到了我早先知道的 react-thunk 库,并开始怀疑从 backend/API 中获取数据的最佳实践是什么? React Hooks 是否改变了这个主题?知道这些很重要吗?
我觉得有点笨但是找不到一篇文章或视频来准确讨论这个话题:)
要执行此最佳实践,请使用以下方法:
我使用了一些包和模式作为最佳实践:
- redux-logger 用于在浏览器控制台中记录操作和状态。
- reselect 选择器可以计算派生数据,允许 Redux 存储最小可能状态等
- redux-thunk Thunks是推荐的basic中间件 Redux 副作用逻辑,包括复杂的同步逻辑 需要访问商店,以及像 AJAX 请求这样的简单异步逻辑 等等
- axios 与 api 一起工作(基于 Promise 的 HTTP 客户端 浏览器和 node.js)
create a directory by name redux or any name of you like in src folder and then create two files
store.js
androotReducer.js
in redux directory. We assume fetch products from API.
要做到这一点:
Create a new directory by name product in redux directory and then create four files by names
product.types.js, product.actions.js, product.reducer.js, product.selector.js
inredux/product
directory
项目结构应该如下
...
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
The
combineReducers
helper function turns an object whose values are different reducing functions into a single reducing function you can pass tocreateStore
.
// 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
在此文件中,我们 select products
和 isFetching
来自商店状态。
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 class分量 在此文件中,我们使用 class 组件
连接到商店和状态// 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 hook) 在这个文件中,我们使用功能组件连接到商店和状态
// 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);