I'm new to React and having trouble creating a simple auth flow using GraphQL, following the React Router example.
I would like / to be my login screen, which appears only if the user is logged out. If not, the app should redirect to /dashboard (which should otherwise be inaccessible). Here's what I have, which generates an infinite loop after authentication:
class App extends Component {
constructor(props) {
super(props);
this.state = {
authenticated: false
}
}
renderButtons () {
if (this.state.authenticated) {
return (
<a>Logout</a>
)
}
return null;
}
componentWillReceiveProps({ data }) {
if (this.props.data.loading && !data.loading) {
if (data.loggedInUser.id && !this.state.authenticated) {
this.setState({authenticated: true});
} else if (this.state.authenticated) {
this.setState({authenticated: false});
}
}
}
render () {
return (
<div className="container">
<nav>
{this.renderButtons()}
</nav>
<GatingRoute path="/" component={LoginPage} isAuthenticated={this.state.authenticated} newPath="/dashboard" />
<PrivateRoute path="/dashboard" component={Dashboard} isAuthenticated={this.state.authenticated} />
</div>
);
}
};
const PrivateRoute = ({ component: Component, isAuthenticated, ...rest}) => (
<Route
{...rest}
render={props => (
isAuthenticated
? (
<Component {...props} />
)
: (<Redirect to={{ pathname: '/', state: { from: props.location } }} />)
)}
/>
);
const GatingRoute = ({ component: Component, isAuthenticated, newPath, ...rest}) => (
<Route
{...rest}
render={props => (
isAuthenticated
? (
(<Redirect to={{ pathname: newPath, state: { from: props.location } }} />)
)
:
<Component {...props} />
)}
/>
);
export default graphql(query, { options: {fetchPolicy: 'network-only'}})(withRouter(App));
Rather than working the way you are now...
In your parent component where you have your routes do the following
<BrowserRouter>
<Route path=“/“ exact render={ ( props ) =>
props.user.loggedIn ? <Dashboard /> : <LoginForm />
}/>
</BrowserRouter>
This assumes the component that renders your router knows of the user status.
My apologies for the poor formatting on mobile.
Related
I am trying to build a NextJS application with AWS Auth. Some pages are protected and others are not. I have defined this as a .secure Component property of each page. This works completely fine in the development environment but after build always re-routes thinking the secure component prop is always true. What am I doing wrong?
An authenticated page (/pages/test-private):
function AuthenticatedTest() {
return (
<Flex w="full" h="full" align="center" justify="center">
<Text>Authenticated Page</Text>
</Flex>
);
}
AuthenticatedTest.title = "Help";
AuthenticatedTest.secure = true;
export default AuthenticatedTest;
An Unauthenticated page (/pages/test-public):
function UnauthenticatedTest() {
return (
<Flex w="full" h="full" align="center" justify="center">
<Text>Unauthenticated Page</Text>
</Flex>
);
}
UnauthenticatedTest.title = "Help";
export default UnauthenticatedTest;
Then, in my _app.tsx file I am checking if the Component has the secure prop. If it does I display the page through a ProtectedRoute which checks if the user is logged in and re-directs if not.
_app.tsx
function ProtectRoute(props: ProtectRouteProps) {
const {user, setUser} = useGlobalState();
const [signedIn, setSignedIn] = useState(false);
const { error } = useToast();
const router = useRouter();
useEffect(() => {
isSignedIn()
.then(async (data) => {
if (data.isSignedIn) {
setUser(data.user);
}
setSignedIn(data.isSignedIn);
})
.catch(() => {
error("You must be signed in to view this page");
router.push("/login");
});
}, [router.route, user, signedIn]);
if (signedIn)
return <>{props.children}</>;
return (
<HStack h="100vh" justify="center" align="center">
<Spinner
thickness="4px"
speed="0.65s"
emptyColor="gray.200"
color="primary-light"
size="lg"
/>
</HStack>
);
}
type MyAppPage<P = any, IP = P> = NextPage<P, IP> & {
title?: string
secure?: boolean
showBackButton?: boolean
page_type?: string,
getLayout?: (page: ReactElement) => ReactNode
}
function MyApp(props: AppProps) {
const [ queryClient ] = useState(() => new QueryClient({
defaultOptions: {
queries: {
staleTime: staleTimes.default,
},
},
}));
const {
Component,
pageProps,
}: { Component: MyAppPage; pageProps: any } = props
const getLayout = Component.getLayout
|| ((page) => <Layout title={Component.title || "Title not set"} showBackButton={Component.showBackButton || false} pageType={Component.page_type || "unkown"}>{page}</Layout>);
return (
<QueryClientProvider client={queryClient}>
<Hydrate state={pageProps.dehydratedState} >
<GlobalStateProvider>
<ChakraProvider theme={theme} >
{Component.secure ? (
<ProtectRoute
page_type={Component.page_type === undefined ? "unkown" : Component.page_type}
>
{getLayout(
<>
<Component {...pageProps} />
<ReactQueryDevtools initialIsOpen={false} position="bottom-right" />
</>
)}
</ProtectRoute>
) : (
getLayout(
<>
<Component {...pageProps} />
<ReactQueryDevtools initialIsOpen={false} position="bottom-right" />
</>
)
)}
</ChakraProvider>
</GlobalStateProvider>
</Hydrate>
</QueryClientProvider>
);
}
MyApp.getInitialProps = async (appContext: any) => {
// calls page's `getInitialProps` and fills `appProps.pageProps`
const appProps = await App.getInitialProps(appContext);
return { ...appProps };
};
export default MyApp
Hello stack overflow I was wondering if its possible to pre-populate with default values upon rendering. I'm also getting an error when using the from react admin. Here's my how i'm using my .
return (
<Edit {...props}>
<SimpleForm>
<TextInput source="audience_name" />
<ReferenceInput label="entity_ids" source="entity_ids" reference="posts">
<EntityInput setEntityLabel={onChangeLabel} onSelectEntity={addEntity} entityNames={entityNames} />
</ReferenceInput>
<br />
<AutocompleteArrayInput
source="tags"
shouldRenderSuggestions={(val) => {
console.log(val);
return val.trim().length > 0;
}}
choices={[
{ id: 'programming', name: 'Programming' },
{ id: 'lifestyle', name: 'Lifestyle' },
{ id: 'photography', name: 'Photography' },
]}
/>
</SimpleForm>
</Edit>
);
};
Try to add the props translateChoice={false}, like:
<AutocompleteInput source="first_name" choices={choices} translateChoice={false}/>
You can try recreating the i18nProvider, like this:
import polyglotI18nProvider from "ra-i18n-polyglot"; // Install this package
import engMessages from "ra-language-english"; // Install this package
const App = () => {
const i18nProvider = polyglotI18nProvider((locale) => engMessages, "en", {
allowMissing: true,
onMissingKey: (key, _, __) => key,
});
return (
<Admin
...
i18nProvider={i18nProvider}
>
)
}
I'm new to react native.
I've created a react native app and my first screen is a login screen. I'm using onChangeText to update state vars with username and password and this works great initially.
However on "logout" when I pop back to the login screen. The inputs still have my username and password in. However the state vars are now back to null.
I've tried setting value to {this.state.username} for the input but this just causes a depth error on state after 2 input presses so doesn't work.
Am I missing something?
import React, { Component } from 'react';
import { View, Text, StyleSheet, Image, Alert, AsyncStorage, Linking } from 'react-native';
import { Input, Left, Spinner, Container, Item, Form, Header, Content, Label, Button } from 'native-base'
export default class Login extends Component {
state = { username: "", password: "", isLoaded: true }
static navigationOptions = {
header: null
}
constructor(props) {
super()
this.state.isLoaded = false
AsyncStorage.getItem("loggedIn").then(res => {
if (res === "true") {
this.props.navigation.navigate('List')
}
else {
this.setState({isLoaded: true})
}
})
}
checkLogin() {
if ((!this.state.username) || (!this.state.password)) {
Alert.alert('Error', 'Username/Password combination unknown', [{
text: 'Okay'
}])
return
}
....... snip ......
if (response === false) {
Alert.alert('Error', 'Username/Password combination unknown', [{
text: 'Okay'
}])
}
else {
AsyncStorage.setItem('user', JSON.stringify(response));
AsyncStorage.setItem('loggedIn', "true");
this.setState({username: null, password: null})
this.props.navigation.navigate('List')
}
}
}
render()
{
if (this.state.isLoaded == false) {
return (
<Container>
<Spinner />
</Container>
)
}
return (
<Container>
<Content>
<Image source={require('../../assets/logo.jpg')}/>
<Form>
<Item floatingLabel>
<Label>Username</Label>
<Input
autoCapitalize='none'
clearButtonMode='always'
onChangeText={text => this.setState({username:text})} />
</Item>
<Item floatingLabel>
<Label>Password</Label>
<Input
secureTextEntry={true}
clearButtonMode='always'
onChangeText={text => this.setState({password: text})} />
</Item>
<Button primary onPress={_ => this.checkLogin()}>
<Text style={styles.loginButtonText}>Login</Text>
</Button>
</Form>
</Content>
</Container>
);
}
}
You can use direct manipulation method.
Try passing ref to Input like ref={ (c) => this._input = c } and then calling the setNativeProps function this._input.setNativeProps({text:''})
I am also using react navigation and face similar issue.
I fixed as below :
import { NavigationEvents } from "react-navigation";
class ... {
onStartScreenFocus = ()>={
this.setState({
username: "", password: ""
})
}
render(){
return(
<View>
<NavigationEvents
onWillFocus={() => this.onStartScreenFocus()}
onDidBlur={() => this.onDidScreenBlur()} />
<View>
)
}
}
I'm new to react-native and formik and I encountered this problem that I'm trying to build up.
How can I fire a function in headerRight using Formik? I have updateCorporation function that will do fire api, and formik will do the job to fire this function and after I press the Update button, but the results are undefined
I didn`t understand why its happening.
File_1.js
const CorporationContainer = (props) => {
const {
navigation,
} = props;
const updateCorporation = (values) => {
// do patch stuff with values
// but its undefined
};
useEffect(() => {
navigation.setParams({ updateCorporation: updateCorporation.bind() });
}, []);
return (
<Corporation
updateCorporation={updateCorporation} />
);
};
CorporationContainer.navigationOptions = ({ navigation }) => ({
headerRight: (
<EditBtn
onPress={() => navigation.state.params.updateCorporation()}
>
<EditText>Update</EditText>
</EditBtn>
),
});
export default CorporationContainer;
File_2.js
const Corporation = (props) => {
const {
updateCorporation,
} = props;
const emailField = useRef(null);
const validationSchema = yup.object().shape({
email: yup.string()
.ensure()
.email('Email must be valid')
.required('Email'),
});
return (
<Formik
initialValues={{
email,
}}
onSubmit={values => updateCorporation(values)}
validateOnBlur={false}
validateOnChange={false}
validationSchema={validationSchema}
>
{(formProps) => {
const {
errors,
setFieldValue,
values,
} = formProps;
return (
<Container>
<Input
name="email"
placeholder="Email Corporation"
textContentType="emailAddress"
keyboardType="email-address"
returnKeyType="done"
autoCapitalize="none"
autoCorrect={false}
ref={emailField}
value={values.email}
onChangeText={setFieldValue}
editable={!email}
error={errors.email}}
/>
</Container>
);
}}
</Formik>
);
};
export default Corporation;
In File_1.js I had to use withForm and remove all Formik things in File_2.js and use the props instead.
const CorporationContainer = (props) => {
const {
navigation,
handleSubmit,
errors,
setFieldValue,
values,
} = props;
useEffect(() => {
navigation.setParams({ updateCorporation: handleSubmit.bind() });
}, []);
return (
<ProfileProfessional
errors={errors}
setFieldValue={setFieldValue}
values={values}
/>
);
};
CorporationContainer.navigationOptions = ({ navigation }) => ({
headerRight: (
<EditBtn
onPress={() => navigation.state.params.updateCorporation()}
>
<EditText>Editar</EditText>
</EditBtn>
),
});
export default withFormik({
// ...
})(CorporationContainer);
Formik author here...
Haven't tried this out, and idk exactly how navigation binding works, but you want to bind Formik's submitForm() prop to the navigation and not the updateCorporation function. However, You will need to do this where you have access to Formik props/context (i.e. as a child of <Formik>).
import React from 'react'
import { connect } from 'formik'
const updateCorporation = (values) => {
// do patch stuff with values
// but its undefined
};
const BindSubmit = connect(({ formik, navigation }) => {
useEffect(() => {
navigation.setParams({ updateCorporation: submitForm.bind() });
}, []);
return null
})
// ... and then just render it somewhere under Formik
const Corporation = () => {
return (
<Formik>
<BindSubmit />
{/* ... same */}
</Formik>
)
}
i'm using a Navigator component and want to pass data from a component to a other.
here is some code.
renderScene(route, navigator) {
var routeId = route.id;
if (routeId === 'SplashPage') {
return (
<SplashPage
navigator={navigator} />
);
}
if (routeId === 'Login') {
return (
<Login
navigator={navigator} />
);
}
if (routeId === 'Home') {
return (
<Home
navigator={navigator} />
);
}
....
I want to pass data from login to home when i click on a button for example
this.props.navigator.replace({
id: 'Home',
passProps: {
currentUser: this.user,
}
})
i would like to get the currentUser in the Home Component. I've tried some code that i found but nothing seem to work.
This is whant i want to do :
class Home extends React.Component {
constructor(props){
super(props);
this.currentUser = ????????
}
If anyone can help me with this i would be grateful.
Thanks
You can set up your renderScene method like this:
renderScene={(route, navigator) => {
return React.createElement(route.component, { ...this.props, ...route.passProps, navigator, route } );
}}
And use it like this:
this.props.navigator.replace({
component: Home,
passProps: {
currentUser: this.user,
}
})
And in Home, you can access currentUser like this:
<Text>{ this.props.currentUser }</Text>
Check out this thread for a more detailed example, but that should work for you.