【问题标题】:React useEffect doesn't dispatch the redux action after page refreshReact useEffect 在页面刷新后不会调度 redux 操作
【发布时间】:2023-07-01 13:29:01
【问题描述】:

我正在尝试从来自 API 的以下数据对象呈现数据。

{
    "code": 0,
    "c": "verified",
    "d": "verified",
    "leaseInfo": {
        "infoId": 6
    },
    "cpfPrice": "500.00",
    "carCurrentLocation": {
        "id": 1,
        "carId": "df47a56a395a49b1a5d06a58cc42ffc4"
    },
    "n": "verified",
    "p": "false",
    "ownerCarInfo": {
        "brand": "Ferrari",
        "model": "0"
    },
    "serviceFeeRate": 0.10,
    "depositPrice": "100.00",
    "pics": [
        {
            "picid": 49,
            "carId": "df47a56a395a49b1a5d06a58cc42ffc4"
        },
    ],
    "items": {
        "itemid": 5,
        "carId": "df47a56a395a49b1a5d06a58cc42ffc4"
    }
}

我正在使用react-redux 来调度一个操作,在该操作中我将获得名为“carDetails”的状态下的数据。

但是,当我尝试访问数据时,如果我的组件被刷新,carDetails 将变为 undefined 并因此给出“Cannot read property ownerCarInfo of undefined.

我在我的 React 组件中像这样获取和解构carDetails 的数据:

import React, {useEffect} from 'react';
import { useDispatch, useSelector } from 'react-redux';

const CarInfo = ({ match }) => {

const dispatch = useDispatch();
const details = useSelector((state) => state.carDetails);

const { loading, carDetails } = details;
const {pics, carCurrentLocation, items, ownerCarInfo} = carDetails;

useEffect(() => {
   dispatch(getCarDetails(match.params.id));
}, [dispatch, match]);

return (
   <div>
  {loading ? (
    <Loader></Loader>
  ) : (
    <>
      <p>{d.depositPrice}</p>
      <p>{ownerCarInfo.brand}</p>
    </>
  )}
</div>

); )

}

只要组件或React 应用程序未刷新,它就会检索数据并正确显示。页面一刷新,carDetails 就变成了一个空数组。

这是getCarDetails() 操作:

export const getCarDetails = (id) => async (dispatch, getState) => {
  try {
    dispatch({
      type: CAR_DETAILS_REQUEST,
    });

    const { userLogin } = getState();
    const { userInfo } = userLogin;

    const config = {
      headers: {
        Authorization: userInfo.token,
        'Content-Type': 'application/json',
      },
    };

    const { data } = await axios.get(
      `${BASE_API}/car/info/getDetails/${id}/${userInfo.bscId}`,
      config
    );

    dispatch({
      type: CAR_DETAILS_SUCCESS,
      payload: data,
    });
  } catch (error) {
    dispatch({
      type: CAR_DETAILS_FAIL,
      payload:
        error.response && error.response.data.msg
          ? error.response.data.msg
          : error.msg,
    });
  }
};

这是我的减速器:

export const carsDetailsReducer = (state = { carDetails: [] }, action) => {
  switch (action.type) {
    case CAR_DETAILS_REQUEST:
      return { loading: true };
    case CAR_DETAILS_SUCCESS:
      return { loading: false, carDetails: action.payload };
    case CAR_DETAILS_FAIL:
      return { loading: false, error: action.payload };
    default:
      return state;
  }
};

这就是我在 redux 存储中声明 carDetails 的方式。

const reducer = combineReducers({
  carDetails: carsDetailsReducer,
});

carDetails 变为未定义且useEffect 未在页面刷新时运行的原因是什么?

【问题讨论】:

  • 你如何在 redux store 中声明carDetails
  • @Viet 我更新了问题中的代码。请检查。
  • getCarDetails 功能一切正常吗?
  • @AmirhosseinEbrahimi 我已经在上面的代码中添加了函数

标签: reactjs redux react-redux react-hooks react-state-management


【解决方案1】:

如果您正在使用 axios,您在调用时使用 async 函数和 await 的操作应如下所示API。

如果您在 api 链接中传递 API 汽车 ID,则在参数中传递 id

import axios from "axios";


export const loadData = (id) => async (dispatch) => {
  dispatch({
       type: "CAR_DETAILS_REQUEST",
    });

  const detailData = await axios.get("http:\\****/id");

  dispatch({
    type: "CAR_DETAILS_SUCCESS",
    payload: {
     success: detailData.data,
    },
  });
};

减速机:

    const initailState = { carDetails: [], loading: true };

export const carsDetailsReducer = (state = initailState, action) => {
  switch (action.type) {
    case CAR_DETAILS_REQUEST:
      return { ...state,
                loading: true
 };
    case CAR_DETAILS_SUCCESS:
      return {...state,
               loading: false,
               carDetails: action.payload 
};
    case CAR_DETAILS_FAIL:
      return { ...state,
               loading: false, 
               error: action.payload };
    default:
      return ...state;
  }
};

您的 useEffect 应该仅在获取数据时起作用:

import React, {useEffect} from 'react';
import { useDispatch, useSelector } from 'react-redux';

const CarInfo = ({ match }) => {

const dispatch = useDispatch();
const details = useSelector((state) => state.carDetails);

const { loading, carDetails } = details;
const {pics, carCurrentLocation, items, ownerCarInfo} = carDetails;

useEffect(() => {
   dispatch(getCarDetails(id));
}, [dispatch]);

return (
   <div>
  {loading ? (
    <Loader></Loader>
  ) : (
    <>
      <p>{d.depositPrice}</p>
      <p>{ownerCarInfo.brand}</p>
    </>
  )}
</div>

您也可以在没有useEffect 的情况下使用它,方法是创建一个 onclick() 函数,如下所示:

const loadDetailHandler = () => {
    dispatch(getCarDetails(id));
  };

return (
    <div onClick={loadDetailHandler} >
    </div>

【讨论】:

  • 这是最终奏效的完整解决方案。谢谢!
  • @DrewReese 是的。为了将它应用到我的用例中,我不得不花一些时间来改进这段代码。我接受这个作为答案的原因是它包含 action reducer 和组件。我认为这可能是一个被接受的答案的好点。我对该网站比较陌生,可能不是最佳选择,我可能会更改已接受的答案并接受高质量的答案。谢谢!
  • @VidarshanAdithya 不用担心。有足够的差异我在围栏上反对它,但既然你已经接受了它,那么它一定是,或者应该是,发现它有帮助/有用。就像我说的那样,我很好奇答案是否涵盖了我以某种方式忽略的方面,如果是的话,如果有必要,我可以在哪些方面进行改进。
【解决方案2】:

如果carDetails 初始状态是一个数组,那么为什么要在 UI 中解构对象属性?再问一次……

如果在重新加载页面后状态恢复到初始状态,空数组仍然是定义的对象。您需要找出导致state.carDetails.carDetails 未定义的原因。如果你检查你的 reducer,注意到你的 CAR_DETAILS_REQUEST case 将 carDetails 状态擦除,它变成了 undefined。老实说,当您的代码在没有重新加载页面的情况下正常运行时,您没有看到这个问题,我很惊讶。

您需要保持这种状态。为了更好地衡量,您应该在计算下一个状态对象时始终浅复制现有状态,除非您有充分的理由省略部分状态。

export const carsDetailsReducer = (state = { carDetails: [] }, action) => {
  switch (action.type) {
    case CAR_DETAILS_REQUEST:
      return {
        ...state, // <-- shallow copy existing state
        loading: true,
      };

    case CAR_DETAILS_SUCCESS:
      return {
        ...state, // <-- shallow copy existing state
        loading: false,
        carDetails: action.payload
      };

    case CAR_DETAILS_FAIL:
      return {
        ...state, // <-- shallow copy existing state
        loading: false,
        error: action.payload,
      };

    default:
      return state;
  }
};

【讨论】:

  • 感谢您指出我在一段时间后意识到的错误。我一直在尝试从数组中解构。然而,浅拷贝确实有帮助。
【解决方案3】:

对我来说,我认为你应该将状态保存在

 `case CAR_DETAILS_REQUEST:
      return {
        ...state, // <-- shallow copy existing state
        loading: true,
      };
`

为了能够在 o 之前使用它,当你想使用减速器时,你应该在每种情况下使用它 让旧状态减速器返回与您也使用的初始状态相同的初始状态正在加载并且在初始状态中未找到
所以尝试制作状态的形状

state={
isloading:false,
 carDetails: []

}

也尝试每次都通过{...state ,is loading:true}来相同的状态

【讨论】:

    【解决方案4】:

    问题出在CAR_DETAILS_REQUEST。你只返回{ loading: true };,所以carDetails会丢失,变成undefined

    只需像这样更新你的减速器:

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

    【讨论】:

      最近更新 更多