组件不重新渲染,reducer 调用后 props 不更新?

Component not re-rendering , after reducer call and props not updating?

我正在尝试从股票 API 中获取一些股票数据。 我可以使用普通的反应钩子获取它,但是在使用 redux 和 redux saga 时我遇到了问题。 在第一个渲染中,它不会更新 redux 存储。 它更新了第二个渲染中的 redux 存储。 但是即使更新了 redux store,我的组件也没有得到更新的状态??

有人可以帮忙吗?

这是我的组件 :

import React, { useState,useEffect } from 'react'
import { connect } from "react-redux";
import { Link } from 'react-router-dom'
function Dashboard(props) {

    useEffect(()=>{
            console.log(props)
            props.getStockData()
    },[])

    useEffect(()=>{
        props.getStockData()
        console.log(props)
    },[props.stocks.length])
  
    return (
        <React.Fragment>
                {props.stocks.length ? 

                    <div> 
                    <h1>hello</h1>
                    </div>
                
                    :   <button class=" p-4 m-4 spinner"></button>
                
                }
        </React.Fragment>
    ) 
   
}
const mapStateToProps = state => {
    return {
      stocks: state
    };
  };
  
const mapDispachToProps = dispatch => {
    return {
      getStockData: () => dispatch({ type: "FETCH_STOCKS_ASYNC" }),
    };
};
export default connect(mapStateToProps,mapDispachToProps)(Dashboard)

这是我的减速器文件:

import axios from 'axios'
import swal from 'sweetalert'
const initialState = {
    stocks:[],
};
const reducer = (state = initialState, action) => {
    const newState = { ...state };
  
    switch (action.type) {
      case 'FETCH_STOCKS_ASYNC':
        console.log('reducer called')
        const getLatestPrice = async()=>{
            await axios.get(`http://api.marketstack.com/v1/eod/latest?access_key=8c21347ee7c5907b59d3cf0c8712e587&symbols=TCS.XBOM`)
            .then(res=>{
                console.log(res.data)
                newState.stocks = res.data
            })
            .catch(err=>{
                console.log(err)
            })
        }
        getLatestPrice();
        
        break;
    }
    return newState;
};
  
export default reducer;

这是我的 redux saga 文件:

import { delay } from "redux-saga/effects";
import { takeLatest, takeEvery, put } from "redux-saga/effects";

function* fetchStockData() {
  yield delay(4000);
  yield put({ type: "FETCH_STOCKS_ASYNC" });
}

export function* watchfetchStockData() {
  yield takeLatest("FETCH_STOCKS_ASYNC", fetchStockData);
}

这是我的商店文件:

import { createStore,applyMiddleware,compose } from 'redux'
import createSagaMiddleware from 'redux-saga'
import { watchfetchStockData } from './sagas'
import reducers from './reducers'


const sagaMiddleware = createSagaMiddleware()
const store = createStore(
        reducers,
        compose(
                applyMiddleware(sagaMiddleware),
               window.devToolsExtension && window.devToolsExtension()
            ),
       
)

console.log(store.getState())

// only run those sagas whose actions require continuous calling.
//sagaMiddleware.run(watchfetchStockData);


export default store;

这是我的索引文件,它将提供程序连接到所有组件:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import './assets/main.css'
import App from './App';
import { Provider } from 'react-redux'
import { BrowserRouter as Router } from 'react-router-dom'
import store from './store'
import * as serviceWorker from './serviceWorker';

ReactDOM.render(
  <React.StrictMode>
  <Provider store={store}>
      <Router>
        <App />
      </Router>
  </Provider>
</React.StrictMode>,
  document.getElementById('root')
);

serviceWorker.unregister();

您需要对减速器中的状态进行深度复制。试试这个:const newState = JSON.parse(JSON.stringify(state))

另外,你的 reducer 不应该有任何 side-effects,它应该只负责更新 store。所以你的 API 调用应该发生在一个异步动作创建者中,然后一旦数据被接收回来,动作应该分派另一个动作来触发 reducer 将数据存储在 redux 中。

你误解了这些部分应该如何组合在一起。我强烈建议您阅读 redux best practices 并了解更多关于减速器做什么和不做什么的信息。 redux reducer 不应执行任何操作。 它仅采用当前状态,returns 下一个状态基于操作对象中的数据。

幸运的是,您已经在使用 redux-saga,它旨在处理 side-effects 等 API 调用。提取需要在您的 saga 中执行 ,而不是在 reducer 中执行。

API 调用通常涉及分派三个操作:开始、成功和失败。从您的组件中,您只会发送“FETCH_STOCKS_START”操作。 saga 通过 takeLatest “采取”这个动作,并使用它来执行具有 call 效果的提取。获取完成后,saga 使用 put 来分派两个结果操作之一。它发送带有包含股票数组的 属性 payload 的类型“FETCH_STOCKS_SUCCESS”,或带有 属性 error 的类型“FETCH_STOCKS_ERROR”包含错误。

function* fetchStockData() {
  yield delay(4000);
  try {
    const res = yield call( axios.get, `http://api.marketstack.com/v1/eod/latest?access_key=8c21347ee7c5907b59d3cf0c8712e587&symbols=TCS.XBOM`);
    yield put({ type: "FETCH_STOCKS_SUCCESS", payload: res.data });
  }
  catch (error) {
    yield put({ type: "FETCH_STOCKS_ERROR", error });
  }
}

function* watchfetchStockData() {
  yield takeLatest("FETCH_STOCKS_START", fetchStockData);
}

相关传奇文档链接:Error Handling and Dispatching Actions

我在状态中添加了一个isLoading 属性,你可以select在true时显示不同的UI。我们将使用所有三个操作更新此 属性。

const initialState = {
  stocks: [],
  isLoading: false
};

您的减速器仅用于根据这些操作更新状态中的原始数据。

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case "FETCH_STOCKS_START":
      return {
        ...state,
        isLoading: true
      };
    case "FETCH_STOCKS_ERROR":
      console.error(action.error); // only temporary since you aren't doing anything else with it
      return {
        ...state,
        isLoading: false
      };
    case "FETCH_STOCKS_SUCCESS":
      return {
        ...state,
        stocks: action.payload,
        isLoading: false
      };
    default:
      return state;
  }
};