redux 状态在 useEffect 和 useSelector 中未定义

redux state is undefined in useEffect and useSelector

我是 redux 的新手。目前我正在做一个项目,通过 API 调用在地图上显示附近的自行车站。所以这意味着大多数 redux 操作都是异步的,并且 returns 一些数据用于下一个 api 在另一个操作中调用。当我在 useEffect 中分派我的所有动作并使用 useSelector 使用 states 时,它会在第一次渲染时显示它们,并在刷新后 返回 undefined这是我在使用 redux、redux-thunk、useEffect、useSelector 时会遇到的常见问题

我已经尝试在 useEffect 挂钩中使用异步函数来等待每个动作调用完成

操作:

export const fetchIPLocation = () => {
    return async (dispatch) => {
        try {
            let response = await fetch(`https://ip.nf/me.json`)
            if(response.ok) {
                let data = await response.json()
                dispatch({
                    type: FETCH_IP_LOCATION,
                    payload: data.ip.ip
                })
            }
        } catch (error) {
            
        }
    }
}

export const fetchUserData = (IPadress) => {
    return async (dispatch) => {
        try {
            let response = await fetch(`http://api.ipstack.com/${IPadress}?access_key=15633b167163620b5cd84ef60db62414`)
            if(response.ok) {
                let data = await response.json()
                dispatch({
                    type: FETCH_USER_DATA,
                    payload: data
                })
            }
        } catch (error) {
            console.log(error)
        }
    }
}

减速器:

import { initialState } from "../store";
import { FETCH_IP_LOCATION, FETCH_USER_DATA, FETCH_NETWORKS } from "../action";

export const rootReducer = (state = initialState, action) => {
    switch (action.type) {
        case FETCH_IP_LOCATION:
            return {
                ...state,
                ipLocation: action.payload,
                isLoading: false,
            }
        case FETCH_USER_DATA:
            return {
                ...state,
                userData: action.payload,
                isLoading: false,
            }
        case FETCH_NETWORKS: 
            return {
                ...state,
                isLoading: false,
                bikeNetworks: action.payload,
            }
    }
}

MapComponent.jsx 呈现传单地图并使用所有状态

import React, { useState } from 'react';
import '../styles/MapComponent.css';
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
import { useEffect } from 'react';
import { fetchIPLocation, fetchUserData, getUserData } from '../redux/action/';
import { useDispatch, useSelector } from 'react-redux'
 
const MapComponent = () => {

    const dispatch = useDispatch()

    const [latitude, setLatitude] = useState(0)
    const [longitude, setLongitude] = useState(0)
    const [checkCords, setCheckCords] = useState(false)

    const ipLocation = useSelector((state) => state.ipLocation)
    const userCountry = useSelector((state) => state.userData.country_code)
    
    console.log(userCountry)

 
    useEffect(() => {
      if(navigator.geolocation) {
          navigator.geolocation.watchPosition((position) => {
              setLatitude(position.coords.latitude)
              setLongitude(position.coords.longitude)
              setCheckCords(true)
          })
      }
    }, [])

    useEffect(async () => {
      await dispatch(fetchIPLocation())
      await dispatch(fetchUserData(ipLocation))
    }, [])

  return (
      !checkCords ? <h1>Loading...</h1> :
    <MapContainer center={[latitude, longitude]} zoom={11}>
      <TileLayer
        attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      />
      <Marker position={[latitude, longitude]}>
        <Popup>
          A pretty CSS3 popup. <br /> Easily customizable.
        </Popup>
      </Marker>
    </MapContainer>
  
  )
}

export default MapComponent

redux 商店:

import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import persistStore from 'redux-persist/es/persistStore';
import storage from 'redux-persist/lib/storage';
import persistReducer from 'redux-persist/es/persistReducer';
import { rootReducer } from '../reducer';

export const initialState = {
    ipLocation: [],
    userData: [],
    bikeNetworks: [],
    bikeStations: [],
    isLoading: true,
}

const persistConfig = {
    key: 'root',
    storage
}

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const persistedReducer = persistReducer(persistConfig, rootReducer)

const configureStore = createStore(
    persistedReducer,
    initialState,
    composeEnhancers(applyMiddleware(thunk))
)

const persistor = persistStore(configureStore)

export { configureStore, persistor }

app.js 和提供者:

import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import MapComponent from './components/MapComponent'
import { configureStore, persistor } from './redux/store';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { BrowserRouter,  Routes, Route } from 'react-router-dom';
import {Container, Row, Col} from 'react-bootstrap';


const App = () => {
  return (
    <Provider store={configureStore}>
      <PersistGate persistor={persistor} loading={null}>
        <Container fluid={true} className='px-0 overflow-hidden'>
          <BrowserRouter>
           <Row>
             <Col md={12}>
               <Routes>
                 <Route exact path='/' element={<MapComponent />} />
               </Routes>
             </Col>
           </Row>
          </BrowserRouter>
        </Container>
      </PersistGate>
    </Provider>
  );
}

export default App;

MapComponent.jsx ipLocation 中,useSelector 在第一个函数(即 fetchIPLocation())之后和第二个函数(即 fetchUserData()).

您需要做的是在从 Actions 文件中的操作 fetchIPLocation 获得响应后调度操作 fetchUserData(ipLocation)

// Action
export const fetchIPLocation = () => {
    return async (dispatch) => {
        try {
            let response = await fetch(`https://ip.nf/me.json`)
            if(response.ok) {
                let data = await response.json()
                dispatch({
                    type: FETCH_IP_LOCATION,
                    payload: data.ip.ip
                })
                dispatch(fetchUserData(data.ip.ip)
            }
        } catch (error) {
            
        }
    }
}

并且在useEffect of MapComponent.jsx中调用dispatch(fetchIPLocation())