【问题标题】:How can I access current redux state from useEffect?如何从 useEffect 访问当前的 redux 状态?
【发布时间】:2025-12-10 12:00:02
【问题描述】:

我有一个从数据库中获取的对象列表(在我的例子中是“相册”)。我需要编辑这些对象。 在 useEffect 挂钩的编辑组件中,我启动了使用其 ID 获取所需专辑的操作。这个动作有效。然而,在同一个 useEffect 中,我试图在触发动作 redux 状态之前获取更改的内容。现在我面临一个问题 - 我所获取的只是 previos 状态。 如何在 useEffect 中实现当前 redux 状态的获取?

我在这里看到过类似的问题,但是没有一个答案对我的用例有帮助。

我正在使用 redux-thunk。

编辑组件。问题出现在 setFormData - 它从减速器获取先前的状态,而不是当前状态。它似乎在状态被 getAlbumById 改变之前触发:

//imports

const EditAlbum = ({
  album: { album, loading},
  createAlbum,
  getAlbumById,
  history,
  match
}) => {
  const [formData, setFormData] = useState({
    albumID: null,
    albumName: ''
  });

  useEffect(() => {
    getAlbumById(match.params.id);

    setFormData({
      albumID: loading || !album.albumID ? '' : album.albumID,
      albumName: loading || !album.albumName ? '' : album.albumName
    });

  }, [getAlbumById, loading]);

const { albumName, albumID } = formData;

  const onChange = e =>
    setFormData({ ...formData, [e.target.name]: e.target.value });

  const onSubmit = e => {
    e.preventDefault();
    createAlbum(formData, history, true);
  };

  return ( //code );
};



EditAlbum.propTypes = {
  createAlbum: PropTypes.func.isRequired,
  getAlbumById: PropTypes.func.isRequired,
  album: PropTypes.object.isRequired
};

const mapStateToProps = state => ({
  album: state.album
});

export default connect(
  mapStateToProps,
  { createAlbum, getAlbumById }
)(withRouter(EditAlbum));

行动:

export const getAlbumById = albumID => async dispatch => {
  try {
    const res = await axios.get(`/api/album/${albumID}`);

    dispatch({
      type: GET_ALBUM,
      payload: res.data
    });
  } catch (err) {
    dispatch({
      type: ALBUMS_ERROR,
      payload: { msg: err.response.statusText, status: err.response.status }
    });
  }
};

减速器

const initialState = {
  album: null,
  albums: [],
  loading: true,
  error: {}
};

const album = (state = initialState, action) => {
  const { type, payload } = action;
  switch (type) {
    case GET_ALBUM:
      return {
        ...state,
        album: payload,
        loading: false
      };
    case ALBUMS_ERROR:
      return {
        ...state,
        error: payload,
        loading: false
      };
    default:
      return state;
  }
};

将不胜感激任何帮助/想法

【问题讨论】:

    标签: reactjs react-redux react-hooks use-effect


    【解决方案1】:

    你应该把你的效果分成2个,一个在专辑ID从路由改变时加载专辑:

    const [formData, setFormData] = useState({
        albumID: match.params.id,
        albumName: '',
    });
    const { albumName, albumID } = formData;
    
    // Only get album by id when id changed
    useEffect(() => {
        getAlbumById(albumID);
    }, [albumID, getAlbumById]);
    

    当数据到达时设置formData状态:

    // Custom hook to check if component is mounted
    // This needs to be imported in your component
    // https://github.com/jmlweb/isMounted
    
    const useIsMounted = () => {
      const isMounted = useRef(false);
      useEffect(() => {
        isMounted.current = true;
        return () => (isMounted.current = false);
      }, []);
      return isMounted;
    };
    
    // In your component check if it's mounted 
    // ...because you cannot set state on unmounted component
    const isMounted = useIsMounted();
    useEffect(() => {
      // Only if loading is false and still mounted
      if (loading === false && isMounted.current) {
        const { albumID, albumName } = album;
        setFormData({
          albumID,
          albumName,
        });
      }
    }, [album, isMounted, loading]);
    

    您的操作应在开始获取相册时将 loading 设置为 true:

    export const getAlbumById = albumID => async dispatch => {
      try {
        // Here you should dispatch an action that would
        //  set loading to true
        // dispatch({type:'LOAD_ALBUM'})
        const res = await axios.get(`/api/album/${albumID}`);
    
        dispatch({
          type: GET_ALBUM,
          payload: res.data
        });
      } catch (err) {
        dispatch({
          type: ALBUMS_ERROR,
          payload: { msg: err.response.statusText, status: err.response.status }
        });
      }
    };
    

    更新检测为什么不应该调用useEffect:

    你能用这个输出更新问题吗?

    //only get album by id when id changed
    useEffect(() => {
      console.log('In the get data effect');
      getAlbumById(albumID);
      return () => {
        console.log('Clean up get data effect');
        if (albumID !== pref.current.albumID) {
          console.log(
            'XXXX album ID changed:',
            pref.current.albumID,
            albumID
          );
        }
        if (getAlbumById !== pref.current.getAlbumById) {
          console.log(
            'XXX getAlbumById changed',
            pref.current.getAlbumById,
            getAlbumById
          );
        }
      };
    }, [albumID, getAlbumById]);
    

    【讨论】:

    • 它确实解决了这个问题,感谢非常详细的解释。进入一个循环,但在第一个 useEffect 中将括号留空 - 效果也很好。
    • 好的,发现另一个问题 - 我现在确实获得了当前数据,但不是在第一次通话时。在第一次调用时,reducer 状态的专辑仍然为 null(在第二个 useEffect 中获取 TypeError:无法读取 null 的属性“albumID”)。刷新编辑页面即可获取到需要的相册。
    • 在第二个 useEffect 中额外检查专辑是否为空,解决了第一次调用的问题
    • @ysvet 第一个 useEffect 不应继续调用自身,除非您进行不必要的卸载和重新安装。我假设albumId 是来自路由器的原始值,而getAlbumById 是对由connect 创建的动作创建函数的相同引用。
    • @ysvet Album 不能为 null 并同时加载 false。除非您遇到错误,否则您不应该尝试显示相册。