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='© <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())
我是 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='© <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())