异步函数上带有 React Native 挂钩 returns 空 object/array 的上下文
Context with React Native hooks returns empty object/array on async function
我已经和这个问题斗争了一段时间了,我似乎不知道如何解决它。
在我的主屏幕上,我获取了用户坐标和一系列电影院。 Cinemas 数组包含每个电影院的坐标,我将其与用户坐标一起使用来计算从用户到电影院的距离。
在 cinemaContext 中获取和存储电影院很好并且可以工作,但是一旦我 运行 计算距离的函数,上下文中的电影院对象是空的。
距离计算为数组中的每个电影院和returns新数组添加一个距离值。
奇怪的是当我尝试时电影上下文对象是空的。然后,如果我在 cinemaContext 或 getUserCoordinates 函数中编辑某些内容并导航到电影院概览屏幕,那么电影院就在那里,具有距离值。
它必须具有加载顺序或异步功能,因为代码“有效”,但似乎没有在正确的时间填充上下文或用空值覆盖它。
我应该补充一点,我在另一个屏幕上使用电影院数组,我可以像这样访问它:
const { state } = useContext(Context)
Home.js
import React, { useState, useEffect, useContext } from "react";
import { View, Text, StyleSheet, TouchableOpacity, StatusBar, ActivityIndicator } from "react-native";
import * as Location from 'expo-location';
import { Context } from "../context/CinemaContext";
const Home = ({ navigation }) => {
const [errorMsg, setErrorMsg] = useState(null);
const { state, updateCinemas, getCinemas } = useContext(Context)
// Fetch user coordinates and call updateCinemas with the coordinates and cinemas
const getUserCoordinates = async (cinemas) => {
try {
const granted = await Location.requestPermissionsAsync();
if (granted) {
let location = await Location.getCurrentPositionAsync({});
await updateCinemas(cinemas, location.coords.latitude, location.coords.longitude)
} else {
throw new Error("Location permission not granted");
}
} catch (e) {
setErrorMsg(e)
}
}
useEffect(() => {
if (state.cinemas.length === 0) {
getCinemas();
}
getUserCoordinates(state.cinemas);
}, []);
if (!state.cinemas) {
return <ActivityIndicator size="large" style={{ marginTop: 200 }} />
}
return ( Some views ..)
CinemaContext.js
import dataContext from "./DataContext";
import _ from "lodash";
import { computeDistance } from "../helpers/utils";
const cinemaReducer = (state, action) => {
switch (action.type) {
case "add_error":
return { ...state, errorMessage: action.payload };
case "get_cinemas":
return { ...state, cinemas: action.payload };
case "update_cinemas":
return { ...state, cinemas: action.payload };
default:
return state
}
};
const getCinemas = dispatch => async () => {
try {
const response = await fetch(
"url-to-cinemas-array",
{ mode: "no-cors" })
const cinemas = await response.json();
dispatch({
type: "get_cinemas",
payload: cinemas
});
} catch (err) {
dispatch({
type: "add_error",
payload: "Something went wrong with the cinemas"
})
}
}
const updateCinemas = (dispatch) => {
return async (cinemas, referenceLat, referenceLong) => {
const cinemasWithDistance = cinemas.map(cinema => {
return {
...cinema,
distance: computeDistance([cinema.geo.latitude, cinema.geo.longitude], [referenceLat, referenceLong]) // Calculate the distance
};
});
const orderedCinemas = _.orderBy(cinemasWithDistance, 'distance');
dispatch({ type: "update_cinemas", payload: orderedCinemas });
}
}
export const { Context, Provider } = dataContext(
cinemaReducer,
{ updateCinemas, getCinemas },
{ cinemas: [], errorMessage: '' }
);
DataContext.js
import React, { useReducer } from 'react';
export default (reducer, actions, defaultValue) => {
const Context = React.createContext();
const Provider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, defaultValue);
const boundActions = {};
for (let key in actions) {
boundActions[key] = actions[key](dispatch);
}
return (
<Context.Provider value={{ state, ...boundActions }}>
{children}
</Context.Provider>
);
};
return { Context, Provider };
};
App.js
import React from "react";
import RootStackNavigator from "./src/navigation/RootStackNavigator";
import { Provider } from "./src/context/CinemaContext";
export default function App() {
return (
<Provider>
<RootStackNavigator />
</Provider>
);
};
非常感谢任何帮助!
提前致谢!
我认为当您调用 getUserCoordinates(state.cinemas);
时 state.cinemas
仍然是空的,因此 "update_cinemas"
操作将 运行 使用空数组,覆盖之前由 "get_cinemas"
。您可以通过在 getUserCoordinates
调用之前添加 console.log('state.cinemas.length: ', state.cinemas.length);
来验证这一点。
我认为一个解决方案是在依赖于 state.cinemas
数组的单独 useEffect
中调用 getUserCoordinates
(这样它每次都会再次 运行s state.cinemas
变化):
useEffect(() => {
if (state.cinemas.length === 0) {
getCinemas();
}
}, []);
useEffect(() => {
if (state.cinemas.length === 0) return; // cinemas not retrieved yet
if (typeof state.cinemas[0].distance !== undefined) return; // distance already computed
getUserCoordinates(state.cinemas);
}, [state.cinemas]);
我已经和这个问题斗争了一段时间了,我似乎不知道如何解决它。
在我的主屏幕上,我获取了用户坐标和一系列电影院。 Cinemas 数组包含每个电影院的坐标,我将其与用户坐标一起使用来计算从用户到电影院的距离。
在 cinemaContext 中获取和存储电影院很好并且可以工作,但是一旦我 运行 计算距离的函数,上下文中的电影院对象是空的。 距离计算为数组中的每个电影院和returns新数组添加一个距离值。
奇怪的是当我尝试时电影上下文对象是空的。然后,如果我在 cinemaContext 或 getUserCoordinates 函数中编辑某些内容并导航到电影院概览屏幕,那么电影院就在那里,具有距离值。
它必须具有加载顺序或异步功能,因为代码“有效”,但似乎没有在正确的时间填充上下文或用空值覆盖它。
我应该补充一点,我在另一个屏幕上使用电影院数组,我可以像这样访问它:
const { state } = useContext(Context)
Home.js
import React, { useState, useEffect, useContext } from "react";
import { View, Text, StyleSheet, TouchableOpacity, StatusBar, ActivityIndicator } from "react-native";
import * as Location from 'expo-location';
import { Context } from "../context/CinemaContext";
const Home = ({ navigation }) => {
const [errorMsg, setErrorMsg] = useState(null);
const { state, updateCinemas, getCinemas } = useContext(Context)
// Fetch user coordinates and call updateCinemas with the coordinates and cinemas
const getUserCoordinates = async (cinemas) => {
try {
const granted = await Location.requestPermissionsAsync();
if (granted) {
let location = await Location.getCurrentPositionAsync({});
await updateCinemas(cinemas, location.coords.latitude, location.coords.longitude)
} else {
throw new Error("Location permission not granted");
}
} catch (e) {
setErrorMsg(e)
}
}
useEffect(() => {
if (state.cinemas.length === 0) {
getCinemas();
}
getUserCoordinates(state.cinemas);
}, []);
if (!state.cinemas) {
return <ActivityIndicator size="large" style={{ marginTop: 200 }} />
}
return ( Some views ..)
CinemaContext.js
import dataContext from "./DataContext";
import _ from "lodash";
import { computeDistance } from "../helpers/utils";
const cinemaReducer = (state, action) => {
switch (action.type) {
case "add_error":
return { ...state, errorMessage: action.payload };
case "get_cinemas":
return { ...state, cinemas: action.payload };
case "update_cinemas":
return { ...state, cinemas: action.payload };
default:
return state
}
};
const getCinemas = dispatch => async () => {
try {
const response = await fetch(
"url-to-cinemas-array",
{ mode: "no-cors" })
const cinemas = await response.json();
dispatch({
type: "get_cinemas",
payload: cinemas
});
} catch (err) {
dispatch({
type: "add_error",
payload: "Something went wrong with the cinemas"
})
}
}
const updateCinemas = (dispatch) => {
return async (cinemas, referenceLat, referenceLong) => {
const cinemasWithDistance = cinemas.map(cinema => {
return {
...cinema,
distance: computeDistance([cinema.geo.latitude, cinema.geo.longitude], [referenceLat, referenceLong]) // Calculate the distance
};
});
const orderedCinemas = _.orderBy(cinemasWithDistance, 'distance');
dispatch({ type: "update_cinemas", payload: orderedCinemas });
}
}
export const { Context, Provider } = dataContext(
cinemaReducer,
{ updateCinemas, getCinemas },
{ cinemas: [], errorMessage: '' }
);
DataContext.js
import React, { useReducer } from 'react';
export default (reducer, actions, defaultValue) => {
const Context = React.createContext();
const Provider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, defaultValue);
const boundActions = {};
for (let key in actions) {
boundActions[key] = actions[key](dispatch);
}
return (
<Context.Provider value={{ state, ...boundActions }}>
{children}
</Context.Provider>
);
};
return { Context, Provider };
};
App.js
import React from "react";
import RootStackNavigator from "./src/navigation/RootStackNavigator";
import { Provider } from "./src/context/CinemaContext";
export default function App() {
return (
<Provider>
<RootStackNavigator />
</Provider>
);
};
非常感谢任何帮助! 提前致谢!
我认为当您调用 getUserCoordinates(state.cinemas);
时 state.cinemas
仍然是空的,因此 "update_cinemas"
操作将 运行 使用空数组,覆盖之前由 "get_cinemas"
。您可以通过在 getUserCoordinates
调用之前添加 console.log('state.cinemas.length: ', state.cinemas.length);
来验证这一点。
我认为一个解决方案是在依赖于 state.cinemas
数组的单独 useEffect
中调用 getUserCoordinates
(这样它每次都会再次 运行s state.cinemas
变化):
useEffect(() => {
if (state.cinemas.length === 0) {
getCinemas();
}
}, []);
useEffect(() => {
if (state.cinemas.length === 0) return; // cinemas not retrieved yet
if (typeof state.cinemas[0].distance !== undefined) return; // distance already computed
getUserCoordinates(state.cinemas);
}, [state.cinemas]);