I'm using React Native 0.36 and Firebase. How do I make the homepage of the app change if a user is logged in. I tried an if statement in renderScene but it gives this error:
index.ios.js
import React, { Component } from 'react';
import {
AppRegistry,
Navigator,
} from 'react-native';
import firebase from 'firebase';
import Login from './Login';
import Home from './Home';
function renderScene(route, navigator) {
return <route.component route={route} navigator={navigator} />;
}
export default class Snip extends Component {
renderScene(route, navigator){
return <route.component navigator={navigator} />
}
render() {
var that = this;
firebase.auth().onAuthStateChanged(function(user) {
if(user) {
return (
<Navigator
initialRoute={{component: Home}}
renderScene={that.renderScene}/>
)
} else{
return (
<Navigator
initialRoute={{component: Login}}
renderScene={that.renderScene}/>
)
}
});
}
}
There are two things that are wrong with the code from the question.
You cannot return a react element inside of a firebase callback
Firebase takes a moment to return if the user exists. So I have to create a loading state that get updated. When firebase is done.
index.ios.js
import React, { Component } from 'react';
import {
AppRegistry,
Navigator,
View,
Text,
} from 'react-native';
import firebase from 'firebase';
import Login from './Login';
import Home from './Home';
function renderScene(route, navigator) {
return <route.component route={route} navigator={navigator} />;
}
export default class Snip extends Component {
constructor(props) {
super(props);
this.state = {
user: null,
loading: false
};
}
componentWillMount() {
var that = this;
firebase.auth().onAuthStateChanged(function(user) {
if(user) {
that.setState({
user: user,
loading: false
})
}else{
that.setState({
loading: false
})
}
});
}
renderScene(route, navigator){
return <route.component navigator={navigator} />
}
render() {
if(this.state.loading){
return <View><Text>loading</Text></View>;
}else if(this.state.user){
return(
<Navigator
initialRoute={{component: Home}}
renderScene={this.renderScene}/>
)
}else{
return (
<Navigator
initialRoute={{component: Login}}
renderScene={this.renderScene}/>
)
}
}
}
Related
My NextJS project has been giving me grief over Graph QL these days. I've been trying to implement an Apollo client solution to retrieve data from a remote GraphQL server into a custom component. But no matter which solution I try, I always end up with this error. Here's my current react-apollo implementation:
// /lib/with-apollo-client.js
import React from "react";
import Head from "next/head";
import { getDataFromTree } from "react-apollo";
import initApollo from "./init-apollo";
export default App => {
return class WithData extends React.Component {
static displayName = `WithData(${App.displayName})`;
static async getInitialProps(ctx) {
const { Component, router } = ctx;
const apollo = initApollo({});
ctx.ctx.apolloClient = apollo;
let appProps = {};
if (App.getInitialProps) {
appProps = await App.getInitialProps(ctx);
}
// Run all GraphQL queries in the component tree
// and extract the resulting data
if (!process.browser) {
try {
// Run all GraphQL queries
await getDataFromTree(
<App
{...appProps}
Component={Component}
router={router}
apolloClient={apollo}
/>
);
} catch (error) {
console.error("Error while running `getDataFromTree`", error);
}
// getDataFromTree does not call componentWillUnmount
// head side effect therefore need to be cleared manually
Head.rewind();
}
// Extract query data from the Apollo store
const apolloState = apollo.cache.extract();
return {
...appProps,
apolloState
};
}
constructor(props) {
super(props);
this.apolloClient = initApollo(props.apolloState);
}
render() {
return <App {...this.props} apolloClient={this.apolloClient} />;
}
};
};
// /lib/init-apollo.js
import { ApolloClient, InMemoryCache, HttpLink } from 'apollo-boost'
import fetch from 'isomorphic-unfetch'
let apolloClient = null
// Polyfill fetch() on the server (used by apollo-client)
if (!process.browser) {
global.fetch = fetch
}
function create (initialState) {
// Check out https://github.com/zeit/next.js/pull/4611 if you want to use the AWSAppSyncClient
return new ApolloClient({
connectToDevTools: process.browser,
ssrMode: !process.browser, // Disables forceFetch on the server (so queries are only run once)
link: new HttpLink({
uri: 'https://api.graph.cool/simple/v1/cixmkt2ul01q00122mksg82pn', // Server URL (must be absolute)
credentials: 'same-origin' // Additional fetch() options like `credentials` or `headers`
}),
cache: new InMemoryCache().restore(initialState || {})
})
}
export default function initApollo (initialState) {
// Make sure to create a new client for every server-side request so that data
// isn't shared between connections (which would be bad)
if (!process.browser) {
return create(initialState)
}
// Reuse client on the client-side
if (!apolloClient) {
apolloClient = create(initialState)
}
return apolloClient
}
The component I'm retrieving data into looks like this:
// /components/PostsList2.jsx
import { Query } from 'react-apollo'
import gql from 'graphql-tag'
export const allUsersQuery = gql`
query allUsers($first: Int!, $skip: Int!) {
allUsers(orderBy: createdAt_DESC, first: $first, skip: $skip) {
id
firstName
createdAt
}
_allUsersMeta {
count
}
}
`
export const allUsersQueryVars = {
skip: 0,
first: 10
}
export default function PostsList2 () {
return (
<Query query={allUsersQuery} variables={allUsersQueryVars}>
{({ loading, error, data: { allUsers, _allUsersMeta }, fetchMore }) => {
if (error) return <aside>Error loading users!</aside>
if (loading) return <div>Loading</div>
const areMorePosts = allUsers.length < _allUsersMeta.count
return (
<section>
<ul>
{allUsers.map((user, index) => (
<li key={user.id}>
<div>
<span>{index + 1}. </span>
<div>{user.firstName}</div>
</div>
</li>
))}
</ul>
{areMorePosts ? (
<button onClick={() => loadMorePosts(allUsers, fetchMore)}>
{' '}
{loading ? 'Loading...' : 'Show More'}{' '}
</button>
) : (
''
)}
</section>
)
}}
</Query>
)
}
function loadMorePosts (allUsers, fetchMore) {
fetchMore({
variables: {
skip: allUsers.length
},
updateQuery: (previousResult, { fetchMoreResult }) => {
if (!fetchMoreResult) {
return previousResult
}
return Object.assign({}, previousResult, {
// Append the new users results to the old one
allUsers: [...previousResult.allUsers, ...fetchMoreResult.allUsers]
})
}
})
}
Since this is a NextJS project, there's also an _app.jsx that I've wrapped in a special provider component:
// /pages._app.jsx
/* eslint-disable max-len */
import '../static/styles/fonts.scss';
import '../static/styles/style.scss';
import '../static/styles/some.css';
import CssBaseline from '#material-ui/core/CssBaseline';
import { ThemeProvider } from '#material-ui/styles';
import jwt from 'jsonwebtoken';
import withRedux from 'next-redux-wrapper';
import App, {
Container,
} from 'next/app';
import Head from 'next/head';
import React from 'react';
import { Provider } from 'react-redux';
import makeStore from '../reducers';
import mainTheme from '../themes/main-theme';
import getSessIDFromCookies from '../utils/get-sessid-from-cookies';
import getLanguageFromCookies from '../utils/get-language-from-cookies';
import getUserTokenFromCookies from '../utils/get-user-token-from-cookies';
import removeFbHash from '../utils/remove-fb-hash';
import withApolloClient from '../lib/with-apollo-client'
import { ApolloProvider } from 'react-apollo'
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
let userToken;
let sessID;
let language;
if (ctx.isServer) {
ctx.store.dispatch({ type: 'UPDATEIP', payload: ctx.req.headers['x-real-ip'] });
userToken = getUserTokenFromCookies(ctx.req);
sessID = getSessIDFromCookies(ctx.req);
language = getLanguageFromCookies(ctx.req);
const dictionary = require(`../dictionaries/${language}`);
ctx.store.dispatch({ type: 'SETLANGUAGE', payload: dictionary });
if(ctx.res) {
if(ctx.res.locals) {
if(!ctx.res.locals.authenticated) {
userToken = null;
sessID = null;
}
}
}
if (userToken && sessID) { // TBD: validate integrity of sessID
const userInfo = jwt.verify(userToken, process.env.JWT_SECRET);
ctx.store.dispatch({ type: 'ADDUSERINFO', payload: userInfo });
}
ctx.store.dispatch({ type: 'ADDSESSION', payload: sessID }); // component will be able to read from store's state when rendered
}
const pageProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {};
return { pageProps };
}
componentDidMount() {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
jssStyles.parentNode.removeChild(jssStyles);
}
// Register serviceWorker
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/serviceWorker.js'); }
// Handle FB's ugly redirect URL hash
removeFbHash(window, document);
}
render() {
const { Component, pageProps, store, apolloClient } = this.props;
return (
<Container>
<Head>
// redacted for brevity
</Head>
<ThemeProvider theme={mainTheme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
<ApolloProvider client={apolloClient}>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</ApolloProvider>
</ThemeProvider>
</Container>
);
}
}
export default withApolloClient(withRedux(makeStore)(MyApp));
So with this setup, when I compile and run my app, it throws the following:
TypeError: Cannot read property 'allUsers' of undefined
I'm really lost! The repo is up at https://github.com/amitschandillia/proost/tree/master/web.
When i do a production build i am getting store not found error. Interestingly Redux store is not being initialised.
The error message:
{ Invariant Violation: Could not find "store" in the context of "Connect(Form(LoginForm))". Either wrap the root
component in a , or pass a custom React context provider to and the corresponding React context consumer to Connect(Form(LoginForm)) in connect options.
import Immutable from 'immutable'
import thunkMiddleware from 'redux-thunk'
import { createLogger } from 'redux-logger'
import { createStore, applyMiddleware, compose } from 'redux'
import config from 'config'
import rootReducer from './reducers/index.js'
function createMiddlewares ({ isServer }) {
let middlewares = [
thunkMiddleware
]
if (config.env === 'development' && typeof window !== 'undefined') {
middlewares.push(createLogger({
level: 'info',
collapsed: true,
stateTransformer: (state) => {
let newState = {}
for (let i of Object.keys(state)) {
if (Immutable.Iterable.isIterable(state[i])) {
newState[i] = state[i].toJS()
} else {
newState[i] = state[i]
}
}
return newState
}
}))
}
return middlewares
}
function immutableChildren (obj) {
let state = {}
Object.keys(obj).forEach((key) => {
state[key] = Immutable.fromJS(obj[key])
})
return state
}
export default (initialState = {}, context) => {
let { isServer } = context
let middlewares = createMiddlewares({ isServer })
let state = immutableChildren(initialState)
return createStore(
rootReducer,
state,
compose(applyMiddleware(...middlewares))
)
}
import withRedux from 'next-redux-wrapper'
import { withRouter } from 'next/router'
import { Provider } from 'react-redux'
import App, { Container } from 'next/app'
import { checkForPopup } from "./helpers/popup.js";
import createStore from './redux/createStore.js'
class MyApp extends App {
static async getInitialProps ({ Component, ctx }) {
return {
pageProps: Component.getInitialProps
? await Component.getInitialProps(ctx)
: {}
}
}
render() {
const { Component, pageProps, store, router } = this.props
return (
<Container>
<Provider store={store}>
<Component router={router} {...pageProps} />
</Provider>
</Container>
);
}
componentDidMount() {
checkForPopup();
}
}
export default withRedux(createStore)(
withRouter(MyApp)
)
Based on #wamba-kevin's comment, when running express in production, you prefix the start script with NODE_ENV=production. I did the same with next and it worked.
I am new to Ionic and I have a problem trying to push a rootpage on Ionic 3
On app.component.ts
import { Component } from '#angular/core';
import { LoginPage } from '../pages/login/login';
import { LoggedinPage } from '../pages/loggedin/loggedin';
import firebase from 'firebase';
#Component({
template: `<ion-nav [root]="rootPage"></ion-nav>`
})
export class MyApp {
rootPage: any;
var state = firebase.auth().onAuthStateChanged(function(user) {
if (user) {
this.rootPage = LoginPage;
//console.log(this.rootPage);
//from here i can see that the this.rootpage is defined.
} else {
this.rootPage = LoggedinPage;
}
console.log(this.rootPage);
//the rootpage is not defined outside of the funtion
});
}
What I want to do, id to redirect users that are already loggedin to the LoggedIn Page.
in app.component.ts
rootPage: any; is mistake, can you use rootPage: any = 'targetPage';
if you use menu rootPage: any = 'menuPage'; and chose rootPage in menuPage.ts ;
export class MenuPage {
rootPage = 'targetPage';
Need to change code following manner:
import {Component, ViewChild} from '#angular/core';
import {Nav} from 'ionic-angular';
import { LoginPage } from '../pages/login/login';
import { LoggedinPage } from '../pages/loggedin/loggedin';
import firebase from 'firebase';
#Component({
template: `<ion-nav [root]="rootPage"></ion-nav>`
})
export class MyApp {
rootPage: any;
#ViewChild(Nav) nav: Nav;
var state = firebase.auth().onAuthStateChanged(function(user) {
if (user) {
this.nav.setRoot(LoginPage);
//console.log(this.rootPage);
//from here i can see that the this.rootpage is defined.
} else {
this.nav.setRoot(LoggedinPage);
}
console.log(this.rootPage);
//the rootpage is not defined outside of the funtion
});
}
You should set userdata after login and clear on logout. and while setting rootPage you can check whether userdata does exist or not. And on initializeApp function of app.component.ts
initializeApp() {
this.platform.ready().then(() => {
this.rootPage = localStorage.getItem('userData') ? MyDashboardPage : LoginPage;
});
}
I have been trying to display the Username of a Logged in user using Tracker React.
I have removed the auto-publish package.
/Client/components/dashboard/sidebar.jsx
import React, {Component} from 'react'
import TrackerReact from 'meteor/ultimatejs:tracker-react';
export default class Sidebar extends TrackerReact(Component) {
constructor() {
super();
this.state = {
subscription: {
users: Meteor.subscribe('users')
}
};
}
render() {
return(
<div>
<h1>{Meteor.user().username}</h1>
</div>
);
}
/Server/publications/userPublications
Meteor.publish("users", function () {
return Meteor.users.find();
});
I am getting a null object while console.log(Meteor.user()). However, I can see the current username using the Meteor DevTools for chrome.
What block of puzzle am I missing?
I doubt you can use Meteor.user(). username
Try this instead
<h1>{{currentUser}}</h1>
You can use createContainer in order to manage it.
import { Meteor } from 'meteor/meteor';
import React, { Component, PropTypes } from 'react';
import { createContainer } from 'meteor/react-meteor-data';
export class Sidebar extends Component {
static propTypes = {
loggedIn: PropTypes.bool.isRequired,
loggingIn: PropTypes.bool.isRequired,
currentUser: PropTypes.shape({
username: PropTypes.string
})
}
render() {
const { loggingIn, loggedIn } = this.props;
return (
<div>
{(() => {
if (loggingIn) {
return <Loading />
} else if (loggedIn) {
// whatever you want here.
} else {
<h1>Please Log In</h1>
}
})()}
</div>
)
}
}
export default createContainer(() => ({
user: Meteor.user(),
loggedIn: !Meteor.userId(),
loggingIn: Meteor.loggingIn()
}), Sidebar);
A few details here:
Meteor.loggingIn() is a reactive variable that is run while the user is logging in. When this is happening, there will not be a user object.
You should create a reactive container
I'm using Meteor/React and I'm trying to display all the users in the system:
I have: /imports/api/users.js
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
if (Meteor.isServer) {
// This code only runs on the server
// Only publish tasks that are public or belong to the current user
Meteor.publish('allUsers', function () {
return Meteor.users.find({}, {fields: {"emails.address": 1}});
});
}
and /imports/ui/App.jsx
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { Meteor } from 'meteor/meteor';
import { createContainer } from 'meteor/react-meteor-data';
class App extends Component {
constructor() {
super();
this.state = {
subscription: {
users: Meteor.subscribe('allUsers')
}
}
}
componentWillUnmount() {
this.state.subscription.users.stop();
}
render() {
let users = this.props.users;
console.log(users);
return (<div>
<h1>GoArc User Manager</h1>
<div>
{users.map((user)=>{
if ('emails' in user ) {
email = user.emails[0].address;
} else {
email = '?'
}
return <div key={user._id}>{user._id} - {email}</div>
})
}
</div>
</div>)
}
}
export default createContainer(() => {
return {
users: Meteor.users.find({ }).fetch(),
};
}, App);
But it still show only the current login user.
If I set autopublish/insecure on then the code is working correct.
What is the correct way to publish all users to the client with Meteor/React?
Another related issue is that even when I set autopublish/insecure on still the email.address field appear only for the current user - even though I published this field:
return Meteor.users.find({}, {fields: {"emails.address": 1}});
The code is correct but I forgot to add import '../imports/api/users.js';
in /server/main.js