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()}
/>
);
}
Related
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}
/>
);
};
In my React native page
Im navigating from one page to another page with parameters
so those parameters has id
which will be used to fetch data from endpoint and display that in flat list
function Assessments ({route,navigation}) {
useEffect(()=>{
fetchData(file)
},[]);
const { file } = route.params;
const [data,setData] = useState([]);
file consists of route params(Id)
and fetchdata function triggers that function with the id and fetches data
const fetchData = async (file) => {
axios.get(`endpoint`)
.then(function (response) {
console.log(response.data)
setData(response.data)
})
.catch(function (error) {
console.log(error);
})
}
and im returning this
return (
<View>
<Text>okay</Text>
<FlatList
flexGrow= {0}
minHeight= '20%'
maxHeight='80%'
data={data}
renderItem={showdata}>
</FlatList>
</View>
)
and renderitem is
const showdata = ({item}) => {
<View>
sdfdsfsdf
</View>
}
but that part isnt even being rendered
not sure where is the issue !
console.log()
{
"id": 19,
"name": "test1",
}
this is how the data from the endpoint is
Your showdata is not returning anything. Please add return to it like this.
Here is the full code.
function Assessments ({route, navigation}) {
const { file } = route.params;
const [data, setData] = useState([]);
useEffect(()=>{
fetchData(file)
},[]);
const fetchData = async (file) => {
axios.get(`endpoint`)
.then(function (response) {
console.log(response.data)
setData(response.data)
})
.catch(function (error) {
console.log(error);
})
}
const showdata = ({ item }) => {
//Add return here
return (
<View>
<Text>
sdfdsfsdf
</Text>
</View>
)
}
return (
<View>
<Text>okay</Text>
<FlatList
//Put all the style within style prop
style={{flexGrow: 0, minHeight: '20%', maxHeight: '80%'}}
data={data}
renderItem={showdata}
>
</FlatList>
</View>
)
}
I'm using axios and redux saga to call api and check with local input, but redux saga always return undefined
Function to get data using axios
export function requestGetUser() {
return axios({
method: 'get',
url: 'https://my-json-server.typicode.com/khanh21011999/demo/user',
});
}
Action file
export const getUser = () => ({
type: actionList.GET_USER,
});
export const setUser = (user) => ({
type: actionList.SET_USER,
user,
});
export const GetUserInfo = (user, password) => {
return{
type: actionList.GET_USER_INFO,
data: {user, password},
}
};
export const LoginSuccess = (data) => {
return {
type: actionList.LOGIN_SUCCESS,
data,
};
};
export const LoginFailed = (data) => {
return {
type: actionList.LOGIN_FAIL,
data,
};
};
export const Logout = (data) => {
return {
type: actionList.LOG_OUT,
data
};
};
Redux-saga part
I log everything but it return undefined
export function* LoginsSagaFunc() {
yield takeLatest('GET_USER_INFO', loginSaga)
}
function* SaveToAsyncStorage(data) {
try {
AsyncStorage.setItem(
'data',
JSON.stringify({
username: data.username,
password: data.password
}))
} catch (e) {
console.log('error save to Storage');
}
}
function* loginSaga(action) {
console.log('Saga is working')
const getJson = yield call(requestGetUser)
const getJsonData = JSON.parse(JSON.stringify(getJson))
const getJsonUsername = String(getJsonData.username)
console.log('JsonUsername '+getJsonUsername)
console.log("local data " + action.data.username)
console.log('getJsonData '+getJsonData)
console.log('GetJson '+getJson)
const getJsonPassword = String(getJsonData.password)
if (String(action.data.username) === getJsonUsername) {
if (String(action.data.password) === getJsonPassword) {
console.log('saga login success')
yield put({type: 'LOGIN_SUCCESS'})
SaveToAsyncStorage(action.data)
}
else {
console.log('saga password fail')
}
}
else {
console.log("saga user fail")
}
}
The reducer
const initStateAuth={
isAuth:false,
isLogIn:false
}
const AuthReducer =(state=initStateAuth,action)=>{
switch (action.type) {
case actionList.LOGIN_SUCCESS:
{
console.log('action : LOG IN SUCCESS');
return {
isAuth: true,
isLogIn: true,
};
}
case actionList.GET_USER_INFO:
{
return initStateAuth
}
case actionList.LOGIN_FAIL:
{
return initStateAuth
}
case actionList.LOG_OUT:
{
return initStateAuth
}
default:
return state;
}
}
export default AuthReducer
How i dispatch on the main file
function LoginScreen({navigation}) {
// set timeout ID for setTimeOut()
const timeIdRef = React.useRef(null);
const dispatch = useDispatch();
const [username, getUsername] = useState('');
const [password, getPassword] = useState('')
// handleInput = (e) => {
// getUserInfo(e.target.value);
// };
// mock user from fake api
useEffect(() => {
// dispatch(getUser());
}, [dispatch]);
dispatch(GetUserInfo(username, password));
// const handlegetdata= ({user,password})=>{
// dispatch(GetUserInfo(user,password))
// // }
// console.log(handleGetdata.user)
const user = useSelector((state) => {
return state.User.user;
});
// console.log('user' + username)
// console.log('userJSon'+user.username)
useEffect(() => {
return () => {
if (timeIdRef.current) {
// make sure this is always cleared in case clearTo is never called
clearTimeout(timeIdRef.current);
}
};
}, [timeIdRef]);
// console.log();
const Login = useSelector((state) => {
return state.LoginAction.loginStatus;
});
// console.log(Login)
// const initModal = false;
// eslint-disable-next-line require-jsdoc
function handleLogin() {
dispatch({type: 'changeLogin'});
}
function handlDefault() {
dispatch({type: 'getDefault'});
}
// not show??
// console.log(username);
// console.log('Login ' + Login)
// const [show, doShow] = useState(initModal);
// const [visible, UpdateView] = useState(false)
// Show modal dialog
// function ChangeModalValue() {
// console.log(show);
// doShow(!show);
// }
// setTimer after Model Appear
function SetTimer() {
handleLogin();
if (timeIdRef.current) {
// clear any previous timeIdRef to avoid multiple button click activate multiple setTimeout
clearTimeout(timeIdRef.current);
}
const timeID = setTimeout(() => {
navigation.navigate('Home');
}, 3000);
timeIdRef.current = timeID;
}
function clearTO() {
clearTimeout(timeIdRef.current);
timeIdRef.current = null;
handlDefault();
}
// make text black when check complete
function getTextStyle(isValid) {
if (isValid) {
return {
color: 'black',
};
}
return {
color: 'grey',
};
}
// function getLoginText() {
// return <CirclesLoader />;
// }
// function hideText(visible){
// if(isDisabler)
// }
const loginValidationSchema = Yup.object().shape({
email: Yup.string().email('Please enter valid email').required('Email Address is Required'),
password: Yup.string()
.min(8, ({min}) => `Password must be at least ${min} characters`)
.required('Password is required'),
});
return (
<View style={styles.ViewStyle}>
<Text style={{fontSize: 40}}>Login To System</Text>
<Formik
validateOnMount
validationSchema={loginValidationSchema}
initialValues={{email: '', password: ''}}
onSubmit={value => {
getUsername(value.email)
getPassword(value.password)
SetTimer()
}}
// () => navigation.navigate('Login')
>
{({handleChange, handleBlur, handleSubmit, values, errors, touched, isValid}) => (
<View>
<TextInput
name="email"
placeholder="Email Address"
style={styles.TextInputForm}
onChangeText={handleChange('email')}
onBlur={handleBlur('email')}
value={values.email}
keyboardType="email-address"
/>
{errors.email && touched.email && <Text style={styles.errorText}>{errors.email}</Text>}
<TextInput
name="password"
placeholder="Password"
onChangeText={handleChange('password')}
onBlur={handleBlur('password')}
value={values.password}
secureTextEntry
style={styles.TextInputForm}
/>
{errors.password && touched.password && (
<Text style={styles.errorText}>{errors.password}</Text>
)}
<TouchableOpacity
onPress={handleSubmit}
style={styles.ButtonLogin}
disabled={!isValid || values.email === ''}>
{/* <CirclesLoader size={20} dotRadius={7} /> */}
<Text style={getTextStyle(isValid)}>Login</Text>
</TouchableOpacity>
<View>
<Modal transparent visible={Login}>
<View
style={{
backgroundColor: '#000000',
flex: 1,
justifyContent: 'center',
alignContent: 'center',
}}>
<View style={styles.ModalStyle}>
<CirclesLoader />
<TextLoader
textStyle={{
fontSize: 25,
marginTop: 20,
}}
text="Logging you in"
/>
<TouchableOpacity onPress={clearTO} style={styles.ButtonBack}>
<Text>Go back</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
</View>
</View>
)}
</Formik>
</View>
);
}
Also, the action get the data when i press, BUT it return undefined in redux-saga part, so username always so equal, what happened??
Why data show in redux debugger but i can't see it on saga, and why data i fetch from axios return undefinded?
a short gif to show what happened
Please help, thank you a lot
FULL CODE : https://codesandbox.io/s/github/khanh21011999/Trainning-react-native
In the axios API call, you need to code for a successful or a failed response as follows:
export function requestGetUser() {
return axios({
method: 'get',
url: 'https://my-json-server.typicode.com/khanh21011999/demo/user',
})
.done (function(data) {
//Get your data here upon successful fetch
})
.fail (function() {
console.log("Failed to fetch data");
})
.always (function() {
console.log("This function always executes whether success or fail");
});
}
First of all your sandbox is not working so make sure it works for all.
Second try using async/await like this in your code, I'm not able to test it because your sandbox is crashing.
export async function requestGetUser() { return await axios.get('https://my-json-server.typicode.com/khanh21011999/demo/user'); }
I have a .map() function with JSX code inside. Although, the JSX is not rendering. It is only rendering after I save the file. I am using expo (React Native).
Here is my code:
import React, { useEffect, useState } from "react";
import * as SecureStore from "expo-secure-store";
import { View, Text, ActivityIndicator } from "react-native";
import { Button } from "react-native-elements";
const Receipts = ({ navigation }) => {
const [receipts, setReceipts] = useState([]);
const [loading, setLoding] = useState(true);
const [result, setResult] = useState({});
const [keys, setKeys] = useState([]);
useEffect(() => {
const getReceiptsData = async () => {
let token = await SecureStore.getItemAsync("token");
console.log(token);
fetch("https://notrealapi/api/receipts", {
method: "GET",
headers: {
Authorization: `JWT ${JSON.parse(token)}`,
},
})
.then((res) => res.json())
.then((json) => {
setReceipts(json);
setLoding(false);
})
.catch((error) => console.error(error));
};
getReceiptsData();
processReceipts();
}, []);
const processReceipts = () => {
const dubps = [];
const resultObj = {};
receipts.map((item) => {
if (dubps.includes(item.merchant_name)) {
resultObj[item.merchant_name] =
resultObj[item.merchant_name] + parseFloat(item.total);
} else {
resultObj[item.merchant_name] = parseFloat(item.total);
dubps.push(item.merchant_name);
}
});
setResult(resultObj);
setKeys(Object.keys(resultObj));
};
const exportReport = async () => {
let token = await SecureStore.getItemAsync("token");
fetch("https://notrealapi/api/export", {
method: "GET",
headers: {
Authorization: `JWT ${JSON.parse(token)}`,
},
})
.then((res) => res.json())
.then((json) => {
console.log(json);
})
.catch((error) => console.error(error));
};
const renderSummary = () => {
return keys.map((key) => {
return (
<View>
<Text
key={key}
style={{
fontSize: 15,
fontWeight: "normal",
paddingBottom: 50,
}}
>
{`You have spent $${result[key].toString()} at ${key}`}
</Text>
</View>
);
});
};
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
{loading ? (
<ActivityIndicator size="large" color="blue" />
) : (
<>
<Text style={{ fontSize: 30, fontWeight: "bold", paddingBottom: 50 }}>
Summary:
</Text>
{renderSummary()}
<Button
type="outline"
title="Export detailed report"
onPress={exportReport}
/>
<Text style={{ fontSize: 10, marginTop: 10 }}>
*The detailed report shall be sent by email.
</Text>
</>
)}
</View>
);
};
export default Receipts;
Note: It does work but only when I save the file and it refreshes using expo CLI. Also, error occurs in the renderSummary() function.
Update: keys can be equal to ["Costco"] and result can be equal to {Costco: 69.99}
You are running processReceipts() before the fetch within getReceiptsData() has resolved.
Notice the order of the console logs in this example.
import React, { useEffect, useState } from "react";
const Receipts = () => {
const [receipts, setReceipts] = useState([]);
const [loading, setLoding] = useState(true);
const [result, setResult] = useState({});
const [keys, setKeys] = useState([]);
useEffect(() => {
const getReceiptsData = async () => {
fetch("https://rickandmortyapi.com/api/character/1", {
method: "GET"
})
.then((res) => res.json())
.then((json) => {
console.log("getReceiptsData resolves");
setReceipts(json);
setLoding(false);
})
.catch((error) => console.error(error));
};
getReceiptsData(); // js won't wait here
processReceipts();
}, []);
const processReceipts = (json) => {
console.log("processReceipts()");
};
return null;
};
export default Receipts;
Instead, handle the data manipulation when the fetch resolves.
import React, { useEffect, useState } from "react";
const Receipts = () => {
const [loading, setLoding] = useState(true);
const [result, setResult] = useState({});
useEffect(() => {
const getReceiptsData = async () => {
fetch("https://rickandmortyapi.com/api/character/1", {
method: "GET"
})
.then((res) => res.json())
.then((json) => {
console.log("getReceiptsData resolves");
processReceipts(json);
setLoding(false);
})
.catch((error) => console.error(error));
};
getReceiptsData();
}, []);
const processReceipts = (json) => {
console.log("processReceipts()");
// do some work and then setResult
};
return null;
};
export default Receipts;
Also, avoid storing a state that is derived from another state where possible. You should either translate the server payload into usable data:
when you receive the payload then set it to state OR
when you are rendering
How can I integrate this class based Axios request to the other hook based code that gets data from a Json file? Basically I want to replace data source LOCATIONS with the Axios get request. Thanks.
componentDidMount () {
axios.get('http://192.168.2.94:3000/list-places').then(res => {
this.setState({
posts: res.data
})
})
}
import { LOCATIONS } from '../data/data'
const HomeScreen = props => {
const renderGridItem = itemData => {
return (
<CategoryGridTile
image={itemData.item.image}
title={itemData.item.title}
onSelect={() => {
props.navigation.navigate({
routeName: 'Categories',
params: {
locationId: itemData.item.id
}
})
}}
/>
)
}
return (
<FlatList
keyExtractor={(item, index) => item.id}
data={LOCATIONS}
renderItem={renderGridItem}
/>
)
}
You just want to get LOCATIONS data from the API? Something like this should work (code is untested)
const HomeScreen = props => {
const [locations, setLocations] = useState(undefined);
useEffect(() => {
async function getLocations() {
const { data } = await axios.get('http://192.168.2.94:3000/list-places');
setLocations(data);
}
getLocations();
}, []);
// ...
return (
<>
{locations && (
<FlatList
keyExtractor={(item, index) => item.id}
data={locations}
renderItem={renderGridItem}
/>
)}
</>
);
};