I'm trying to have a header button in my screen that access the screen state and validates if a post is ready to send to the DB (ie. its not an empty string and if theres an image post the image too).
When i create a full post (image and text) and try and fire the post function in the screen, it works (see the 'test' button on the screen)
When i create a full post (image and text) and try and fire the post function from the header, it does not grab the state properly.
What is happening here? How do i resolve this?
Screen State & Header
function PostScreen(props) {
const [newPost, setNewPost] = useState('')
const [isLoading, setIsLoading] = useState(false)
const [mediaAsset, setMediaAsset] = useState('')
React.useLayoutEffect(() => {
props.navigation.setOptions({
headerRight: () => {
return (
isLoading ? <ActivityIndicator color='#4292CE' size='small' style={{ marginRight: 10 }} /> :
<TouchableOpacity style={styles.postButton} onPress={() => postFunction()}>
<Text style={styles.postText}>Post</Text>
</TouchableOpacity>)
},
title: null
});
}, [isLoading, newPost]);
Post Function
let postFunction = async () => {
setIsLoading(true)
if (newPost === '') {
console.log(newPost) // this comes up as nothing when the postFunction is executed from the header
}
else if (mediaAsset === '') {
//! post only the text
}
else if (mediaAsset != ''){
//! post the text and the image
}
setIsLoading(false)
}
Test Environment
Related
I'm new to react-native and when i was creating my app i couldn't get auto add button disabling to work
I set up these properties as a hooks:
const [taskText, setTaskText] = useState(null);
const [isDisabled, setDisabled] = useState('true');
then i have my function:
const addActivity = () => {
if (taskText == null){
setDisabled('true');
}else{
setDisabled('false');
}
}
but when i use isDisabled on touchableOpacity's disable prop its not updating
textInput prop :
onChange={() => addActivity()}
TouchableOpacity:
<TouchableOpacity style={styles.addWrapper} onPress={() => handleNewTask()} disabled={isDisabled}>
<Text style={styles.addText}>+</Text>
</TouchableOpacity>
setTaskText: (called by TextInput)
onChangeText={text => setTaskText(text)}
Doesn't TextInput update itself when i delete all the text?
What do i do to fix this?
It is because you use : useState('true') and you always use a string in your state.
A string, when it is not empty, is always true.
You must use the boolean values not string. You can therefore change to : useState(true) and change addActivity to :
const addActivity = () => {
if (taskText == null){
setDisabled(true);
}else{
setDisabled(false);
}
}
I'm trying to make a login page and I want to display an error if the login fails.
I call the function here: onClick={ this.login }
But I want the function to display the error message without re-rendering everything. The function is inside a class
Try something like this:
const [username, setUsername] = useState('');
const [error, setError] = useState('');
const handleLogin = async () => {
// request a login to your server
const res = await fetch('http://yourapi.com/login');
// determine if the request was successful
if (res.error) {
setError('Invalid input.')
}
else {
// continue
}
};
return (
<Form onSubmit={handleLogin}>
<span>{error}</span>
<input onChangeText={text => setUsername(text)}/>
</Form>
);
Found out how to do it by creating an invisible alert that's visible when I set a variable to the error
{
this.state.status &&
<Alert severity="error">
<AlertTitle>Error</AlertTitle>
{ this.state.status }
</Alert>
}
I have a react login page with username and password manually inputted,
I want the submit button to automatically click upon page load so it will automatically log in users.
Here is my code.
export const LoginForm = () => {
const history = useHistory();
const classes = useStyles();
const email = 'email#email.com';
const password = 'mypassword';
const [authStatus, setStatus] = useState(null);
const [isSpinnerHidden, setSpinner] = useState(true);
const { session } = useContext(SessionContext);
const recaptchaRef = useRef();
const _onSubmit = async (e) => {
e.preventDefault();
setSpinner(false);
const authResult = await session.signIn(email, password);
setSpinner(true);
console.log(session);
authResult ? setStatus(authResult) : history.push('/dashboard');
};
return (
<form onSubmit={_onSubmit}>
<Typography
align='center'
color='primary'
variant="h4"
style={{ marginBottom: '20px' }}
>
Sign In
</Typography>
{authStatus ?
<Alert
severity="error"
>
{authStatus}
</Alert>
:
null
}
<TextField
type='email'
label="Email"
/>
<TextField
type='password'
label="Password"
/>
<Link
onClick={() => history.push('/restore')}
className={classes.restore}>
Forgot password?
</Link>
<MuiButton
text='Sign In'
type='submit'
value='login'
isSpinnerHidden={isSpinnerHidden}
className={classes.button}
/>
</form>
);
};
what can I add to this code or change that will make it click on the sign-in button automatically and login the user.
In order to automatically click on the submit button, you need to use an onLoad
convert your _onSubmit to a windows onload, something like this
window.onload = async (e) => {
e.preventDefault();
setSpinner(false);
console.log('hello');
const authResult = await session.signIn(email, password);
setSpinner(true);
console.log(session);
authResult ? setStatus(authResult) : history.push('/dashboard');
}
I don't think clicking the button automatically is the best experience you can have here.
The better way of doing that here would be to conditionally choose the route based on authentication state before you even render the login screen. Something like this:
if (isAuthenticated){
history.push('/dashboard);
} else {
history.push('/login');
}
If for whatever reason you NEED to do it your way, just call your signin function when the components mounts with useEffect
import React, {useEffect} from 'react';
export const LoginForm = () => {
...
useEffect(() => {
if(isAuthenticated){ // You'll need to get the auth state from somewhere here
history.push('/dashboard')
}
},[])
return (
...
);
};
I have a screen where I am using a query like this:
export const AllFriends: React.FunctionComponent = () => {
const navigation = useNavigation();
const { data, error } = useGetMyProfileQuery({
onCompleted: () => {
console.log('hellooo')
},
});
Every time I visit this page from the home-page, I think the query re-runs as I always see the hellolog. Similarly, from this page, I visit another screen like this:
<Text
onPress={() => navigation.navigate('PendingRequests')}>
Pending Requests (
{data
? data.me.pendingUserRelationsRequestsReceived.totalCount
: ''}
)
</Text>
Every time I visit this screen, I see the hellooo from pending again. This screen looks like this:
export const ReceivedPendingRequests: React.FunctionComponent = () => {
const navigation = useNavigation();
const { data, error } = useGetMyProfileQuery({
onCompleted: () => {
console.log('hellooo from pending')
},
});
return (
<SafeAreaView style={styles.safeView}>
<Container style={styles.container}>
<Text
style={styles.backText}
onPress={() => navigation.navigate('AllFriends')}>
Zurück
</Text>
</Container>
</SafeAreaView>
);
};
Now the problem is that when I navigate back to AllFriends, the query should re-run and I should see the hello log again just like I see when I come from the Homepage to AllFriends. But I don't.
However, if I come back from AllFriends to PendingRequests, I see the log hellooo from pending again.
Edit:
useFocusEffect(()=>{
getMyProfile()
},[]);
const getMyProfile = () => {
const { data, error } = useGetMyProfileQuery({
onCompleted: () => {
console.log('hellooo')
},
//fetchPolicy: 'network-only',
});
}
You have to call refetch it will re-run the query for you. You can pass it to other screens aswell.
You can get it like this:
const {loading, data, refetch} = useQuery(Query, {})
Now in you useFocuseEffect just call this:
useFocusEffect(()=>{
React.useCallback(() => {
refetch();
}, [])
},[]);
when using navigation.navigate the screen doesn't "unmount", so it will not be reinitialized as fresh from the start as the screen does when you're using navigation.replace
Try using navigation.replace instead of navigation.navigate. I hope it helps, if not, let me know.
I just updated to react native navigation version 5. Now I am trying to send data back to previous screen on goBack() call.
I push next view with
const onSelectCountry = item => {
console.log(item);
};
navigation.navigate('SelectionScreen', {
onSelect: onSelectCountry});
And making move back after selecting item from FlatList with call:
function onSelectedItem(item) {
route.params.onSelect(item);
navigation.goBack();
}
But by sending function over with params I get a warning: Non-serializable valuse were found in the navigation state...
Can someone please tell me correct way to do this.
heres is an implementaion
scereen A
const Screen1 = ({navigation, route}) => {
const [item, setItem] = useState(null);
useEffect(() => {
navigation.addListener('focus', () => {
console.log(route.params)
})
}, [])
const onPress = () => {
navigation.navigate('Screen2', {onReturn: (item) => {
setItem(item)
}})
}
return (
// Components
)
}
Screen2:
const Screen2 = ({navigation, route}) => {
useEffect(() => {
navigation.addListener('focus', () => {
console.log(route.params)
})
}, [])
// back Press
const onPress = () => {
route.params.onReturn(item);
navigation.goBack()
}
return (
// Components
)
}
navigation send data to screens.
onPress={() => {
// Pass params back to home screen
navigation.navigate('Home', { post: postText });
follow official documentation React Native
I visited this post because I wanted to use the same common component in 2 stacks. How to know how to go back and pass params?
I solved it by passing first a parameter to go there, which will identify where the component is accessed from.
Then, instead of using goBack(), which doesn't accept parameters, I navigate to the previous route and pass the parameter.
//in Stack1
navigation.navigate('commonComponent', isStack1: true)
//in Stack2
navigation.navigate('commonComponent', isStack1: false)
//in CommonComponent, instead of goback(), use navivation.navigate
function CommonComponent({ navigation, route }) {
const {isStack1} = route.params
const customRoute = isStack1 ? 'routeNameInStack1' : 'routeNameInStack2'
return (
<Button title="Go back" onPress={() => navigation.navigate('customRoute', {myParams: 'blabla...') />
)
}