组件不重新渲染,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;
}
};
我正在尝试从股票 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;
}
};