如何使用useEffect从Redux获取更新状态

发布时间:2020-07-07 13:43

我正在建立一个MERN Stack在线商店网站,并且正在我的Shoes.js组件中的useEffect挂钩中获取产品。但是我只能从redux获取初始状态,而不是更新状态。

数据可以很好地获取,但我只能访问初始状态。因此,传递给ProductsArea组件的值为false和null如何获取更新状态?

这是我的Shoes.js文件:

import React, { useEffect } from 'react';
import './Shoes.css';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';


import { getProducts } from '../../../actions/productsActions';
import ProductsArea from './ProductsArea';
import Navbar from '../landing/Navbar';
import Search from './Search';
export const Shoes = (props) => {

  useEffect(() => {
    props.getProducts();

    console.log(props.products);
    console.log(props.loading);
  }, []); 
  if(props.loading) {
    return (
      <h1>loading</h1>
    )
  }

  else {
    return (
      <div>
        <Navbar />
        <div className="shoes">
          <Search />
          <h1 className="productsTitle">Our Selection</h1>
            <ProductsArea loading={props.loading} products={props.products} /> 
            {/* {
              props.products.map(product => (
                <ProductCard key={product._id} product={product} />
              ))
            } */}
        </div>
      </div>
    )
  }
}

const mapStateToProps = state => ({
  products: state.products.products,
  loading: state.products.loading
})

export default connect(mapStateToProps, { getProducts })(Shoes);

这是我的产品操作文件

import {GET_PRODUCTS, SET_LOADING, SET_ERROR} from './types';

export const getProducts = () => async (dispatch) => {
  try{
    setLoading();
    const res = await fetch('http://localhost:5000/products');
    const data = await res.json();
    console.log(data);
    dispatch({
      type: GET_PRODUCTS,
      payload: data
    });
  }
  catch(err) {
    dispatch({
      type: SET_ERROR,
      payload: err
    })
  }
}

export const setLoading = () => {
  console.log('Loading true');
  return {
    type: SET_LOADING
  }
}

这是getProductsReducer文件:

import {GET_PRODUCTS, SET_LOADING, SET_ERROR} from '../actions/types';
const initialState = {
  products: [],
  loading: false,
  error: null
}

export default (state = initialState, action) => {
  switch (action.type) {
    case GET_PRODUCTS: 
    console.log(action.payload);
      return {
        ...state,
        products: action.payload,
        loading: false
      }

    case SET_LOADING: 
      return {
        ...state,
        loading: true
      };

    case SET_ERROR: 
      console.log(action.payload);
      return {
        ...state,
        error: action.payload
      };
    default: return state;
  }
}

这是我的redux的index.js文件:

import {combineReducers} from 'redux';
import getProductReducer from './getProductReducer';

export default combineReducers({
  products: getProductReducer
});

还有Store.js文件:

import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const initialState = {};

const middleware = [thunk];

const store = createStore(rootReducer, initialState, composeWithDevTools(applyMiddleware(...middleware)));

export default store;

所以我检查了redux扩展名,状态显示在Home.js页面上,而不显示在Shoes.js文件中

这是Home.js文件:

import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { getProducts, setLoading } from '../../../actions/productsActions';
import { connect } from 'react-redux';
import {Link} from 'react-router-dom';
import './Home.css';
import Navbar from './Navbar';
export const Home = (props) => {

  useEffect(() => {
    props.setLoading();
    props.getProducts();
    //eslint-disable-next-line
    console.log(props.products);
    console.log(props.loading);
  }, []); 
  if(props.loading) {
    return <div>loading</div>
  }
  else {
  return (
      <div>
        <Navbar />
        <div className="home">
          <div className="group-1">
            <div className="branding">
              <div className="brandName">
                The
                <br/>
                Sole
                <br/>
                Store
              </div>
              <div>
                  <p>The finest designs and fits.</p>
              </div>
            </div>
            <div className="viewProducts">
              <div>
                <p>
                  Check out our latest and greatest models
                </p>
                <Link className="productsBtn" to="/shoes">GO <i className="fas fa-arrow-right"/></Link>
              </div>
            </div>
          </div>
          <div className="group-2">
            <div className="products">
              <div className="product"></div>
              <div className="product"></div>
              <div className="product"></div>
              <div className="product"></div>
            </div>
            <div className="something"></div>
          </div>
        </div>
      </div>
    )
  }
}

Home.propTypes = {
  products: PropTypes.object.isRequired,
  loading: PropTypes.bool.isRequired
}

const mapStateToProps = state => ({
  products: state.products.products,
  loading: state.products.loading
});

export default connect(mapStateToProps, {getProducts, setLoading})(Home);

尽管如此,我仍然只能从Home.js的控制台中获取初始状态,而不是更新状态。

我已经进行了@ Kalhan.Toress建议的更改,这是更新的Shoes.js文件

import React, { useEffect } from 'react';
import './Shoes.css';
// import { Link } from 'react-router-dom';
import { connect } from 'react-redux';

import { getProducts } from '../../../actions/productsActions';
import ProductsArea from './ProductsArea';
import Navbar from '../landing/Navbar';
import Search from './Search';
export const Shoes = (props) => {

  useEffect(() => {  
    props.fetchData();
    console.log(JSON.parse(props.products.products));
  }, []); 

  if(props.loading) {
    return (
      <h1>loading</h1>
    )
  }

  else {
    return (
      <div>
        <Navbar />
        <div className="shoes">
          <Search />
          <h1 className="productsTitle">Our Selection</h1>
            <ProductsArea loading={props.loading} products={JSON.parse(props.products.products)} /> 
            {/* {
              props.products.map(product => (
                <ProductCard key={product._id} product={product} />
              ))
            } */}
        </div>
      </div>
    )
  }
}

const mapDispatchToProps = dispatch => {
  return {
    fetchData: () => dispatch(getProducts())
  };
};

const mapStateToProps = state => ({
  products: state.products.products,
  loading: state.products.loading
})

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

我可以从首页单击“鞋子”页面的链接,并且一切正常,但是一旦我重新加载Shoes.js页面或直接进入它,这就是我得到的错误: 错误:引发了跨域错误。 React无法访问开发中的实际错误对象。

这是我的服务器端的App.js文件,在其中我启用了CORS:

const express = require('express');
const app = express();
const bodyParser = require('body-parser')
const productRoute = require('./products/productRoute');
const orderRoute = require('./orders/orderRoute');
const userRoute = require('./users/userRoute');
const adminRoute = require('./admins/adminRoute');
app.use((req, res, next) => {
    res.header('Access-Control-Allow-Origin','*');
    res.header('Access-Control-Allow-Headers','Origin, X-Requested-With, Content-Type, Authorization, Accept');
    if(res.method === 'OPTIONS') {
        res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, PATCH, DELETE');
    }
    next();
});
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));

app.use('/products', productRoute);
app.use('/orders', orderRoute);
app.use('/users', userRoute);
app.use('/admin', adminRoute);


app.use((req, res, next) => {
    const error = new Error();
    error.status = 404;
    next(error);
});

app.use((error, req, res, next) => {
    res.status(error.status || 500 ).json({
        error: error
    })
});

module.exports = app;

非常感谢您的帮助! 谢谢!

回答1

我认为您调度同步操作的方式不正确

通过调用props.getProducts();会返回一个同步功能,如我所见,它不会触发任何调度动作

const getProducts = () => async (dispatch) => {
  try{
  ....

确保按如下所示放置console.log并进行检查

useEffect(() => {
    const returnedFromAction = props.getProducts();
    console.log(returnedFromAction); // this should prints the returned function and it will not get dispatched
    ....
}

在这里,您需要通过执行以下返回函数来调度同步操作

您必须添加一个mapDispatchToProps如下

....
const mapDispatchToProps = dispatch => {
  return {
    fetchData: () => dispatch(getProducts())
  };
};    

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

然后在useEffect内部使用此fetchData函数调度提取操作,因此现在在useEffect

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

这将为您完成工作,我为您创建了一个示例演示,请查看here

这可以通过不使用redux hooks来与您的方法保持一致,但是还有另一种方法可以轻松实现,如下所示。


import { useDispatch } from 'react-redux'; // import the dispatcher

const App = props => {
  const dispatch = useDispatch(); // get a reference to dispatch
  useEffect(() => {
    dispatch(getProducts()); // dispatch the action
  }, []);

查看in here