How can i pass data from react-native to react-native-webview? - reactjs

I am trying to get a token from Firebase and pass the token to the webview.
The blog says to use webviewRef.current.postMessage, but that function is not executed. How can I pass the token?
Below is my code.
const App = () => {
useEffect(() => {
const firebase = async () => {
try {
const token = await messaging().getToken(); // i want to pass this token to webview
} catch (error) {
}
};
firebase();
}, []);
return (
<SafeAreaView style={{flex: 1}}>
<WebView
startInLoadingState={true}
source={{uri: 'http://myurl'}}
/>
</SafeAreaView>
);
};

Use can pass data to web-view from react-native by using injectJavascript method like the code below. Ref https://github.com/react-native-webview/react-native-webview/blob/4c050771757f4f7d92cf816bf0a5bf16c5539c07/docs/Reference.md#injectjavascriptstr
import React, { useRef } from 'react';
import { WebView } from 'react-native-webview';
const MyWebView = (props) => {
const webviewRef = useRef(null);
const injectedJavaScript = `
function setData(data) {
window.data = data;
}
`;
return (
<WebView
source={{ uri: 'https://example.com' }}
injectedJavaScript={injectedJavaScript}
onLoad={() => {
webviewRef.current.injectJavaScript(`setData(${JSON.stringify(props.data)});`);
}}
ref={webviewRef}
/>
);
};

Related

Invalid use of Hooks when using onPress to call a function

I ran into this issue of Invalid hook call. The AltIconButton is a component that I place in the export default function with redirect={GoogleLogin}
Here is my login.js snippet:
const AltIconButton = (props) => {
console.log(props.name);
return (
<TouchableOpacity activeOpacity={0.5} onPress={props.redirect}>
<MaterialCommunityIcons
style={{
marginHorizontal: 15,
}}
name={props.name}
size={48}
color="white"
/>
</TouchableOpacity>
);
};
Then this is my google_login:
function GoogleLogin() {
const navigation = useNavigation();
const [request, response, promptAsync] = Google.useIdTokenAuthRequest({
expoClientId: Constants.manifest.extra.google.WEB_CLIENT_ID,
});
useEffect(() => {
if (response?.type === "success") {
const { id_token } = response.params;
const credential = Firebase.auth.GoogleAuthProvider.credential(id_token);
Firebase.auth()
.signInWithCredential(credential)
.then(() => {
navigation.replace("Home");
});
}
}, [response]);
return;
}
EDIT:
This is another snippet of where I implement my AltIconButton Component
<View style={styles.bottomBody}>
<AltIconButton name="facebook" redirect={FBLogin}></AltIconButton>
<AltIconButton name="google"redirect={GoogleLogin}></AltIconButton>
</View>
By changing the JS function into its own component solve the problem.
For example:
function GoogleLogin() {
const navigation = useNavigation();
const [request, response, promptAsync] = Google.useIdTokenAuthRequest({
expoClientId: Constants.manifest.extra.google.WEB_CLIENT_ID,
});
useEffect(() => {
if (response?.type === "success") {
const { id_token } = response.params;
const credential = Firebase.auth.GoogleAuthProvider.credential(id_token);
Firebase.auth()
.signInWithCredential(credential)
.then(() => {
navigation.replace("Home");
});
}
}, [response]);
return (
<TouchableOpacity
disabled={!request}
activeOpacity={0.5}
onPress={() => promptAsync()}
/>
);
}

How to implement infinite list using React Query in react native with Flatlist

Here is my code:
import React, {useState} from 'react';
import {useQuery, useInfiniteQuery} from 'react-query';
import {getMeetup} from '../../api/methods/getMeetups';
export default function MyFunction(props) {
const [next, setNext] = useState('');
const fetchData = async ({pageParam = ''}) => {
const response = await getMeetup( pageParam);
console.log('API RESP', response);
return response;
};
const {data, isLoading, fetchNextPage} = useInfiniteQuery(
'myData',
fetchData,
{
getNextPageParam: (lastPage, pages) => lastPage?.next?._id,
},
);
console.log('RQUERY CHECK', data);
const getMore = () => {
console.log('data end', data?.pages[0]?.next?._id);
fetchNextPage({pageParam: data?.pages[0]?.next?._id});
};
const flattenData = data?.pages
? data?.pages?.flatMap((page) => [...page.Docs])
: [];
return (
<View>
<FlatList
style={{
marginBottom: verticalScale(40),
paddingHorizontal: scale(15),
}}
data={flattenData}
keyExtractor={(item) => item._id}
renderItem={({item, index}) => {
return <ListItem data={item} index={index} />;
}}
onEndReachedThreshold={0.1}
onEndReached={getMore}
/>
</View>
);
}
The problem i am facing is when the page loads the api calls one by one with unique next ids or page param. What i was trying to implement is , when user reaches the end of the page (onEndReached) the data needs to be fetched with a new page param.
getNextPageParam return the value for the next page. So you don't need pass pageParam in fetchNextPage unless you want overwrite, for any reason, the next page value.
You can add a hasNextPage validation for unnecessary requests.
const {data, isLoading, hasNextPage, fetchNextPage} = useInfiniteQuery( /* your code */ )
const getMore = () => {
if(hasNextPage)
fetchNextPage();
};

react native: "Can't perform a React state update on an unmounted component" trouble with useContext

I am new to programming and I am having a problem with my react native app.
after pressing twice on the on/off Button the app stops working and this is the error that I get:
"Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in %s.%s, a useEffect cleanup function,
in Provider (at App.js:40)
in _default (at withExpoRoot.js:22)"
My guess is that I need to do some kind of clean up but I don't know how to implement it in my code.
this is my code:
import React, { useContext, useEffect } from 'react';
import { Text, StyleSheet, Button, TouchableOpacity } from 'react-native';
import { Context as PContext } from '../context/PContext';
const PDetailScreen = (navigation) => {
const { state, fetchSensore } = useContext(PContext);
const { addMotorStateApp } = useContext(PContext);
useEffect(() => {
fetchSensore(() => {
return state;
});
}, []);
const onPress = () => {
if (state.data.waterMotor) {
addMotorStateApp(state.data._id, !state.data.waterMotor.state);
}
fetchSensore();
};
return (
<>
{state.data && (
<>
<Text style={{ fontSize: 28 }}>
Muister sensor data:
{state.data &&
state.data.muisterSensor.tests[
state.data.muisterSensor.tests.length - 1
].status}
</Text>
</>
)}
<Button
title="Get Data"
style={styles.button}
onPress={() =>
fetchSensore((state) => {
return state;
})
}
/>
{state.data && (
<TouchableOpacity style={styles.button} onPress={onPress}>
{state.data.waterMotor.state === 'true' ? (
<Text>ON</Text>
) : (
<Text>OFF</Text>
)}
</TouchableOpacity>
)}
</>
);
};
PDetailScreen.navigationOptions = () => {
title: 'Plnati';
};
const styles = StyleSheet.create({
button: {
alignItems: 'center',
backgroundColor: '#00CC00',
padding: 10,
},
});
export default PDetailScreen;
I will be grateful if someone explain to me how to solve this problem.
edit-
my Context provider code is this:
import createDataContext from './createDataContext';
import ApiConnect from '../api/ApiConnect';
const URL = '/api/v1/p;
const pReducer = (state, action) => {
switch (action.type) {
case 'fetch_Sensore':
return action.payload;
case 'Motor_State':
console.log(action.payload);
return action.payload;
default:
return state;
}
};
const fetchSensore = (dispatch) => async () => {
const response = await ApiConnect.get(`${URL}/Data`);
dispatch({ type: 'fetch_Sensore', payload: response.data });
};
const addMotorStateApp = (dispatch) => async (id, stateMotor) => {
const response = await ApiConnect.post(`${URL}/motorstateapp`, {
id,
stateMotor,
});
dispatch({ type: 'Motor_State', payload: response.data });
};
export const { Provider, Context } = createDataContext(
pReducer,
{
fetchSensore,
addMotorStateApp,
},
[]
);

React Native fetch before render components

I am creating a Reat Native app which connects to an API from which it gets data.
I am using React Navigation to handle navigation. The app has a Stack Navigator and a Bottom Tab Navigator. The StackNavigator has 4 screens:
SignupScreen which handles creating account;
LoginScreen for handlong log in;
SplashScreen that checks for a local token and logs in the user automatically;
A LoadingScreen that triggers the initial fetch call to the API, stores the response in state and navigates to the MainFlow screen;
A MainFlow screen that contains the TabNavigator.
The TabNavigator has two screens, FeedScreen, Account and More where the initial screen is FeedScreen.
The signup/login/local flows are all working fine.
The issue: Once the user is logged in successfully the LoadingScreen is triggering the API call but the MainFlow components are being rendered before the data is in state. Because the components in MainFlow need the data, an error is thrown. How can I render the FeedScreen components only once the data is there?
In the LoadingScreen I am triggering an API call on useEffect from a context object, QuestionContext:
const LoadingScreen = ({ navigation }) => {
const [loading, setLoading] = useState(true);
const { state: authState } = useContext(AuthContext);
const { getQuestionsForUser, getAllQuestions } = useContext(QuestionContext);
useEffect(() => {
getAllQuestions();
}, []);
return (
<View style={styles.container}>
<YonStatusBar backgroundColor="#310B3B" />
<Image source={splashLogo} containerStyle={styles.splashLogo} />
<ActivityIndicator />
</View>
);
};
export default LoadingScreen;
getAllQuestions is a function in QuestionContext which makes the API call and navigates to FeedScreen:
const getAllQuestions = (dispatch) => {
return async () => {
try {
const token = await AsyncStorage.getItem('token');
const config = { headers: { Authorization: `Bearer ${token}` } };
const response = await yonyonApi.get(`/questions`, config);
dispatch({ type: 'GET_ALL_QUESTIONS', payload: response.data });
RootNavigation.navigate('MainFlow');
} catch (e) {
console.log(e);
}
};
};
getAllQuestions is working fine: the API call is successful and I can see that the response is stored in state. However, it navigates to MainFlow before that happens.
Finally, this is the FeedScreen:
const FeedScreen = () => {
const { state: questionState } = useContext(QuestionContext);
return (
<ScrollView style={styles.container}>
{console.log(questionState.questions)}
<View style={styles.listContainer}>
<QuestionCard />
</View>
</ScrollView>
);
};
export default FeedScreen;
The FeedScreen renders a QuestionCard which needs the data in questionState. This is what throwing the error: the QuestionCard is being rendered before the data is in state.
How can I make the navigation only navigate to FeedScreen once the necessary data is in state? Or alternatively, render something else than the QuestionCard while the data is not there and once the data is in questionState render the QuestionCard?
For me i will use screen instead of two screens as follows :
const FeedScreen = () => {
const [loading, setLoading] = useState(true);
const { state: authState } = useContext(AuthContext);
const [data, setData] = useState([]);
const getAllQuestions = (dispatch) => {
return async () => {
try {
const token = await AsyncStorage.getItem('token');
const config = { headers: { Authorization: `Bearer ${token}` } };
const response = await yonyonApi.get(`/questions`, config);
setData(response.data)
setLoading(false)
} catch (e) {
console.log(e);
}
};
};
useEffect(() => {
getAllQuestions();
}, []);
return (
<ScrollView style={styles.container}>
{
(loading)?
<ActivityIndicator/>
:
<View style={styles.listContainer}>
<QuestionCard data={data}/>
</View>
}
</ScrollView>
);
};
export default FeedScreen;
Why don't you set the initial state of your context to null and render your component if it is not null ?
const [questionState, setQuestionState] = useState(null);
...
const FeedScreen = () => {
const { state: questionState } = useContext(QuestionContext);
return (
<ScrollView style={styles.container}>
{!!questionState?.questions && console.log(questionState.questions)}
<View style={styles.listContainer}>
<QuestionCard />
</View>
</ScrollView>
);
};
export default FeedScreen;

How to use async/await with useEffect React hook, i tried a lot of examples and nothing is working

I need to implement async/await with useEffect React hook. I tried a lot of ways. Every time i have an error: Hooks can only be called inside the body of a function component.
import React, { useEffect } from 'react'
import { ActivityIndicator, StatusBar, StyleSheet, View } from 'react-native'
import * as Keychain from 'react-native-keychain'
import useEffectAsync from '../utils'
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center'
}
})
const AuthLoadingScreen = ({ navigation }) => {
useEffect(() => {
const fetchData = async () => {
const data = await Keychain.getGenericPassword()
console.log('data', data)
navigation.navigate(data ? 'App' : 'Auth')
}
fetchData()
}, [])
const { container } = styles
return (
<View style={container}>
<ActivityIndicator />
<StatusBar barStyle="default" />
</View>
)
}
export { AuthLoadingScreen }
Everything looks good, but try this also,
export const AuthLoadingScreen = ({ navigation }) => {...}
Or
const AuthLoadingScreen = ({ navigation }) => {...}
export default AuthLoadingScreen;
I would wrap it in an anonymous function and have it called immediately:
I might try this:
const AuthLoadingScreen = ({ navigation }) => {
useEffect(() => {
(async () => {
const data = await Keychain.getGenericPassword()
console.log('data', data)
navigation.navigate(data ? 'App' : 'Auth')
})()
}, [])
const { container } = styles
return (
<View style={container}>
<ActivityIndicator />
<StatusBar barStyle="default" />
</View>
)
}
If it worked, do let me know because I am learning React Hooks as well :D.

Resources