I'm doing the verification of the phone number, and I have to pass the phone number to the other checkCode.js component.
I have seen examples that pass it navigate() as a pramas, but how can I receive it in another component.
register.js
const SignUp = ({ navigation }) => {
const [phoneNumber, setPhoneNumber] = useState('');
let register = "https://app.herokuapp.com/api/v1/auth/register"
let sendVerification = "https://app.herokuapp.com/api/v1/auth/sendVerification-otp"
const signUp = () => {
const userParams = {
phone: phoneNumber,
};
const requestOne = axios.post(register, userParams)
const requestTwo = axios.post(sendVerification, userParams)
axios
.all([requestOne, requestTwo], userParams)
.then(axios.spread((...responses) => {
navigation.navigate('CodeVerification')
}))
.catch((err) => {
console.log('the error:', err.message);
})
}
checkCode.js
export default function CodeVerification({navigation}) {
//need phoneNumber param in this component
const [code, setCode] = useState('');
const confirm = () =>{
const userParams = {
phone: "+11111111",
code:code,
};
axios
.post('https://app.herokuapp.com/api/v1/auth/sendVerification-otp', userParams)
.then((response) =>{
console.log('response', response.data);
navigation.navigate('Welcome')
})
.catch((error) => {
console.log('the error:', error.message);
});
};
How can I pass it?
This might help
register.js
const SignUp = ({ navigation }) => {
// existing code remains the same
const signUp = () => {
....
axios
.all([requestOne, requestTwo], userParams)
.then(
axios.spread((...responses) => {
// send params like this
navigation.navigate("CodeVerification", {phone: phoneNumber});
})
)
.catch((err) => {
console.log("the error:", err.message);
});
};
};
checkCode.js
export default function CodeVerification({ route, navigation }) {
// get phoneNumber from props
const {phone} = route.params; // UPDATED this line
const [code, setCode] = useState("");
....
}
You can use Context Api
Context api is commonly used for transferring data to another component.
Related
Suppose I have a component that loads its content when an asynchronous call returns succesfuly:
const MyScreen = () => {
let userData: userDataResponse;
const [email, setEmail] = useState("");
const [firstTime, setFirstTime] = useState(true);
async function localGetUserData() {
userData = await getUserData();
setEmail(userData.email);
setFirstTime(false);
}
useEffect(() => {
localGetUserData();
}, []);
if (firstTime) {
return <Text>Cargando...</Text>;
}
return (
<SafeAreaView style={styles.formStyling}>
When the data is available, it sets a state variable so the real content renders
If I want to test it, I think I should mock the getUserData so the mocked function returns a mocked email, say {email: a#b.c}
What would be a good approach to achieve this?
Assuming following component setup (as I cannot see whole component):
myScreenUtils.js
export const getUserData = async () => {
return Promise.resolve('original implementation')
}
MyScreen.jsx
import { useState, useEffect } from "react";
import { getUserData } from './myScreenUtils.js'
const MyScreen = () => {
let userData;
const [email, setEmail] = useState("");
const [firstTime, setFirstTime] = useState(true);
async function localGetUserData() {
userData = await getUserData();
setEmail(userData.email);
setFirstTime(false);
}
useEffect(() => {
localGetUserData();
}, []);
if (firstTime) {
return <div>Cargando...</div>;
}
return (
<div>{email}</div>
)
};
export default MyScreen;
You can write following tests:
import { screen, render, waitFor, waitForElementToBeRemoved } from '#testing-library/react';
import MyScreen from "../MyScreen";
import * as utils from '../myScreenUtils';
describe('MyScreen', () => {
it('the text is displayed and then removed', async () => {
jest.spyOn(utils, 'getUserData').mockResolvedValue({ email: 'mocked value' });
render(<MyScreen />);
expect(screen.getByText('Cargando...')).toBeInTheDocument();
await waitForElementToBeRemoved(() => screen.queryByText('Cargando...'))
})
it('the text email is fetched and displayed', async () => {
jest.spyOn(utils, 'getUserData').mockResolvedValue({ email: 'mocked value' });
render(<MyScreen />);
await waitFor(() => {
expect(screen.getByText('mocked value')).toBeInTheDocument()
})
})
})
Inside App.js I have auth validation (i am using useState, useMemo, useEffect) but when tried to impement splash screen and following Splas screen Dos I am getting Rendered more hooks than during the previous render. So following Rules of Hooks I put at top level useEffect and useState but now I am getting a new error 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 App I see I need to cancel async functions but I need them to request the server and validate users.
This is how my code was before implementing Splash screen:
export default function App() {
const [auth, setAuth] = useState(undefined);
useEffect(() => {
(async () => {
const token = await getTokenApi();
if (token) {
setAuth({
token,
idUser: jwtDecode(token).id,
});
} else {
setAuth(null);
}
})();
}, []);
const login = (user) => {
setTokenApi(user.jwt);
setAuth({
token: user.jwt,
idUser: user.user.id,
});
};
const logout = () => {
if (auth) {
removeTokenApi();
setAuth(null);
}
};
const authData = useMemo(
() => ({
auth,
login,
logout,
}),
[auth]
);
if (auth === undefined) return null;
return (
<AuthContext.Provider value={authData}>
<PaperProvider>{auth ? <AppNavigation /> : <Auth />}</PaperProvider>
</AuthContext.Provider>
);
This is how i got it now
export default function App() {
const [auth, setAuth] = useState(undefined);
useEffect(() => {
(async () => {
const token = await getTokenApi();
if (token) {
setAuth({
token,
idUser: jwtDecode(token).id,
});
} else {
setAuth(null);
}
})();
}, []);
const [appIsReady, setAppIsReady] = useState(false);
useEffect(() => {
async function prepare() {
try {
await SplashScreen.preventAutoHideAsync();
await Font.loadAsync(Entypo.font);
await new Promise((resolve) => setTimeout(resolve, 4000));
} catch (e) {
console.warn(e);
} finally {
setAppIsReady(true);
}
}
prepare();
}, []);
const login = (user) => {
setTokenApi(user.jwt);
setAuth({
token: user.jwt,
idUser: user.user.id,
});
};
const logout = () => {
if (auth) {
removeTokenApi();
setAuth(null);
}
};
const authData = useMemo(
() => ({
auth,
login,
logout,
}),
[auth]
);
if (auth === undefined) return null;
const onLayoutRootView = useCallback(async () => {
if (appIsReady) {
await SplashScreen.hideAsync();
}
}, [appIsReady]);
if (!appIsReady) {
return null;
}
return (
<View onLayout={onLayoutRootView}>
<AuthContext.Provider value={authData}>
<PaperProvider>{auth ? <AppNavigation /> : <Auth />}</PaperProvider>
</AuthContext.Provider>
</View>
);
}
I'm new to react native and currently trying to make an app with firebase v9 for practice. I'm kinda stuck with the hook issue, as it says:
[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 a useEffect cleanup function.]
This is the Login Component:
const LoginScreen = ( {navigation}) => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [storeKey, setStoreKey] = useState([]);
const [userName, setName] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [image, setImage] = useState(null);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (authUser) => {
if(authUser){
AfterLogin();
}
});
return unsubscribe;
}, []);
const signInUser = () => {
signInWithEmailAndPassword(auth, email, password)
.then(async (re) => {
//setIsSignedIn(true);
AfterLogin();
})
.catch((error) => {
console.error(error);
})
}
const AfterLogin = () => {
if(!storeKey || !userName || !image){
getData(setStoreKey, setName, setIsLoading, setImage);
}
navigation.replace('Drawer', {
storeKey: storeKey,
userName: userName,
image: image,
});
}
And this is the getData Function:
const getData = async(setStoreKey, setName, setIsLoading, setImage) => {
const auth = getAuth();
const user = auth.currentUser;
if(user !== null){
const email = user.email;
const UserInfo = await getDoc(doc(db, 'users', email));
if(UserInfo.exists()){
setStoreKey(UserInfo.data().storeKey)
setName(UserInfo.data().name);
setIsLoading(false)
setImage(UserInfo.data().postImage)
}
else{
console.log('None')
}
return
}
}
I guess the problem is happening in the useEffect in Login, but I don't know how to solve this :S
EDIT: I think the problem is from getData function. When I comment out getData function in AfterLogin, it works fine without error :S
but I don't know how it causes error.
Is it because it's async function?
Try this
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (authUser) => {
if(authUser){
AfterLogin();
}
});
return () => {
unsubscribe();
}
}, []);
When i try to set state from API, i have response.data but setState dont work.
const [items, setItems] = useState([]);
useEffect(() => {
cargarItems();
}, [])
const cargarItems = async () => {
const res = await fetch('http://localhost:4000/api/items');
const data= await res.json();
setItems(data);
console.log(data);
console.log(items); }
Also try:
await axios
.get('http://localhost:4000/api/items')
.then((res) => {
console.log(typeof res.data);
setItems(res.data);
})
.catch((err) => {
console.log(err);
});
console.log(items);
And allways i get:
I have the response but the state is not updated.
Thanks in advance
I'm taking the images I uploaded to cloud storage, but the problem is the variable is not an array, so it is only storing just one url. How do I make variables with state array?
My code:
const reference = storage().ref('images');
const [imageUrl, setImageUrl] = useState();
const refer = storage().ref('images');
useEffect(() => {
try {
listFilesAndDirectories(reference).then(() => {
console.log('Finished listing');
});
refer.list().then(result => {
result.items.forEach(element => {
element.getDownloadURL().then(downloadUrl => {
setImageUrl(downloadUrl)
console.log(imageUrl)
console.log("=================")
}).catch(error =>{
alert(error)
})
})
})
} catch (error) {
alert(error);
}
}, []);
Is that what you are looking for?
const [items, setItems] = useState([]);
const handleStateChange = () => {
setItems(state => [...state, 'someNewItem']);
}
With useCallback
const handleStateChange = useCallback(function () {
setItems(state => [...state, 'someNewItem']);
}, [])