react-native navigating between screens from non component class - firebase

I'm trying to navigate between react native screens from my Backend class like this:
var self = this;
firebase.auth().onAuthStateChanged((user) => {
if (user) {
self.setState({
userID: user.uid,
})
} else{
self.props.navigation.navigate("Login");
}
});
My backend class is not a component and therefore is not imported into the stack navigator I am using. I am getting an error saying 'self.props.navigation is not an object'.
Does anyone know I can fix this? Thanks

One not-so-good practice is to define your Navigator as a static/class variable of your App instance:
const MyNavigator = StackNavigator(...);
export default class MyApp extends Component {
render() {
return <MyNavigator ref={(ref) => MyApp.Navigator = ref}/>
}
}
then you can access your navigator and it's props and functions anywhere you want! (for example dispatch a back event):
import MyApp from '...';
MyApp.Navigator.dispatch(NavigationActions.back());

I am personally not a fan of navigation actions happening at that level however, sometimes it's necessary. Expanding on the answer from #Dusk a pattern was made known to me that helps with this very solution. You can find it here
https://github.com/react-community/react-navigation/issues/1439#issuecomment-303661539
The idea is that you create a service that holds a ref to your navigator. Now from anywhere in your app you can import that service and have access to your navigator. It keeps it clean and concise.

If you are using react-navigation then you can achieve this via Navigation Service
Create a file named NavigationService and add the below code there
import { NavigationActions, StackActions } from 'react-navigation';
let navigator;
function setTopLevelNavigator(navigatorRef) {
navigator = navigatorRef;
}
function navigate(routeName, params) {
navigator.dispatch(
NavigationActions.navigate({
routeName,
params
})
);
}
function goBack(routeName, params) {
navigator.dispatch(
StackActions.reset({
index: 0,
actions: [
NavigationActions.navigate({
routeName,
params
})
]
})
);
}
function replace(routeName, params) {
navigator.dispatch(
StackActions.replace({
index: 0,
actions: [
NavigationActions.navigate({
routeName,
params
})
]
})
);
}
function pop() {
navigator.dispatch(StackActions.pop());
}
function popToTop() {
navigator.dispatch(StackActions.popToTop());
}
// add other navigation functions that you need and export them
export default {
navigate,
goBack,
replace,
pop,
popToTop,
setTopLevelNavigator
};
Now import this file in your app.js and set the TopLevelNavigator, your app.js will look something like this
import React, { Component } from 'react';
import NavigationService from './routes/NavigationService';
export default class App extends Component {
constructor() {
super();
}
render() {
return (
<View style={{ flex: 1, backgroundColor: '#fff' }}>
<AppNavigator
ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef);
}}
/>
</View>
);
}
}
Now you are good to go, you can import your NavigationService where ever you want, you can use it like this in any of the components and non-component files
import NavigationService from 'path to the NavigationService file';
/* you can use any screen name you have defined in your StackNavigators
* just replace the LogInScreen with your screen name and it will work like a
* charm
*/
NavigationService.navigate('LogInScreen');
/*
* you can also pass params or extra data into the ongoing screen like this
*/
NavigationService.navigate('LogInScreen',{
orderId: this.state.data.orderId
});

Related

Use auto import for dinamyc components in Nuxt 3

I'm trying to create some dynamic components.
I have this method:
//makeComponent.ts
export default function makeComponent(module) {
return {
setup(_props, { slots }) {
return () => h('div', { class: Object.values(module) }, slots);
},
}
}
Then I call it In a file (index.js) inside my components' folder.
//components/index.ts
import {makeComponent} from './makeComponent';
import nameModule from './styles/name.module.styl';
export const Name = makeComponent(nameModule);
My problem is this doesn't work with Nuxt's auto-import.
Is there any way to achieve this with auto-import?
Thanks in advance for any clues on that.

Stencil JS - How to also distribute a set of shared methods

I have this simple component that is compiled to a web-component by Stencil:
import { Component, h, Prop } from "#stencil/core";
import { IAuthLoginConfig } from "../../interfaces";
import { initAuth } from "../../services";
#Component({
tag: "login-button",
styleUrl: "login-button.css",
})
export class LoginButton {
#Prop() public baseUrl: string = "/oidc/v1/authorize";
#Prop() public config: IAuthLoginConfig;
render() {
return (
<button onClick={() => initAuth(this.config, this.baseUrl)}>
Sign in
</button>
);
}
}
On the button click a shared function initAuth(...) is called that this is imported from the services-directory:
import { IAuthLoginConfig } from "../interfaces";
export const initAuth = (authConfig: IAuthLoginConfig, baseUrl: string): void => {
const url = `${baseUrl}${buildParameters(authConfig)}`;
window.location.href = url
};
const buildParameters = ({ oauth2, oidc }: IAuthLoginConfig) => {
return 'some parameters';
};
Is there any (standard Stencil) way to also build and publish this file so that a user of our web-component library can import the exported functions and use/call them? In our use-case an end-user should be able to use methods in his/her own application directly that are also used in our web-components.
Other use-cases: shared variables, classes...
Thanks in advance!
You will have to manually export each object you want to be accessible in ./src/index.ts, e.g.:
export { initAuth } from './services';
// or
export * from './services';
This allows you to import it in the consuming project:
import { initAuth } from 'your-installed-package';
// or (depending on how you published)
import { initAuth } from 'your-installed-package/dist';

How to Debug White Screen Page (No Content Showing) in RN Expo App with No Error Prompts

I've been building an app in React Native Expo. First, I incorporated Facebook Login simply by copying and pasting the login async code into Login.js and added this.login() to componentWillMount. This worked - With the Facebook login popup showing up as app loads. I was able to log into my FB account with a success message.
However, as soon as I tried to incorporate Firebase, particularly somewhere between transferring code between my Home.js page and the Login.js page, I started getting this white screen to appear on page load.
There are no errors in a terminal; except a message that FacebookAppID and facebookDisplayName do not belong in app.json.
I tried adding a different background color (black) in CSS, which works, but still, there is no content.
Removing FacebookAppID and facebookDisplayName from app.json, which did nothing.
Updating my App Key to the correct one (I was missing the last number).
Restarted the terminal, expo web terminal x code and metro builder several times.
Updated my code so that every file in my Screens directory has { connect } & { login } imports as well as functionMapStateToProps and export default connect statements at bottom.
I tried changing a tab in TabNavigator.js to Login page, and using "Login" as the initialRouteName, but got an error that Login.js isn't a React component.
The first page that should show up before any other is the Facebook login...So it would seem the issue is there.
App.js
import React from 'react';
import Login from './screens/Login';
import reducers from './redux/reducers';
import thunkMiddleware from 'redux-thunk';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
const middleware = applyMiddleware(thunkMiddleware);
const store = createStore(reducers, middleware);
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<Login/>
</Provider>
);
}
}
------ end of App.js ------------
Login.js
import React from 'react';
import styles from '../styles'
import RootNavigator from '../navigation/RootNavigator';
import { connect } from 'react-redux';
import { login } from '../redux/actions';
import * as firebase from 'firebase';
import firebaseConfig from '../config/firebase.js';
firebase.initializeApp(firebaseConfig)
import {
Text,
View,
TouchableOpacity
} from 'react-native';
class Login extends React.Component
state = {}
componentWillMount() {
firebase.auth().onAuthStateChanged((user) => {
if (user != null) {
this.props.dispatch(login(true))
console.log("We are authenticated now!" + JSON.stringify(user));
}
});
}
login = async () => {
const { type, token } = await Expo.Facebook.logInWithReadPermissionsAsync('YourAppKeyGoesHere', {
permissions: ['public_profile'],
});
if (type === 'success') {
// Build Firebase credential with the Facebook access token.
const credential = await firebase.auth.FacebookAuthProvider.credential(token);
// Sign in with credential from the Facebook user.
firebase.auth().signInWithCredential(credential).catch((error) => {
// Handle Errors here.
Alert.alert("Try Again")
});
}
}
render() {
if(this.props.loggedIn){
return (
<RootNavigator/>
)
} else {
return (
<View style={styles.container}>
<TouchableOpacity onPress={this.login.bind(this)}>
<Text>{this.props.loggedIn}</Text>
</TouchableOpacity>
</View>
)
}
}
}
function mapStateToProps(state) {
return {
loggedIn: state.loggedIn
};
}
export default connect(mapStateToProps)(Login);
---------end of Login.js ----------
Home.js
import React from 'react';
import styles from '../styles';
import { connect } from 'react-redux';
import { login } from '../redux/actions';
import {
Text,
View,
Alert
} from 'react-native';
class Home extends React.Component {
state = {}
componentWillMount() {
}
render() {
return (
<View>
<Text>Home</Text>
</View>
)
}
}
function mapStateToProps(state) {
return {
loggedIn: state.loggedIn
};
}
export default connect(mapStateToProps)(Home);
-----end of Home.js ------
redux folder
actions.js
export function login(){
return function(dispatch){
dispatch({ type: 'LOGIN', payload: input });
}
}
----end of actions.js ----
reducers.js
export default reducers = (state = {
loggedIn: false,
}, action) => {
switch (action.type) {
case 'LOGIN': {
return { ...state, loggedIn: action.payload }
}
}
return state;
}
------end of reducers.js ------
-----end of redux folder ------
-----navigation folder (react navigation) -------
---RootNavigator.js---
import React from 'react';
import TabNavigator from './TabNavigator';
import {
createDrawerNavigator,
createStackNavigator,
createBottomTabNavigator,
createAppContainer,
} from 'react-navigation';
const AppNavigator = createStackNavigator(
{
Main: {
screen: TabNavigator,
},
}
);
const AppContainer = createAppContainer(AppNavigator);
export default class RootNavigator extends React.Component {
render() {
return <AppContainer/>;
}
}
----end of RootNavigator.js-----
----TabNavigator.js----
import React from 'react';
import Home from '../screens/Home';
import Profile from '../screens/Profile';
import Matches from '../screens/Matches';
import {
createDrawerNavigator,
createStackNavigator,
createBottomTabNavigator,
createAppContainer,
createMaterialTopTabNavigator,
} from 'react-navigation';
export default createBottomTabNavigator(
{
Profile: {
screen: Profile,
navigationOptions: {
tabBarLabel: 'Profile',
},
},
Home: {
screen: Home,
navigationOptions: {
tabBarLabel: 'Home',
}
},
Matches: {
screen: Matches,
navigationOptions: {
tabBarLabel: 'Matches',
},
},
},
{
navigationOptions: {
header: null
},
tabBarPosition: 'top',
initialRouteName: 'Home',
animationEnabled: true,
swipeEnabled: true,
tabBarOptions: {
style: {
height: 75,
backgroundColor: 'blue'
},
}
}
);
-----end of TabNavigator----
Have you tried remote js Debugging?
What you can do is, Debugg JS remotely.
https://developers.google.com/web/tools/chrome-devtools/remote-debugging/
try to console.log("hi"); when your first component of your app mounts.
Try to add it in login page when the login component mounts.
That will help you debug unseen error which gets listed in the js debugger.
Just check those errors and follow up!
You're good to go!
I was also getting splash logo white screen, tired possible solution nothing works out, at last I have remove node_module and yarn.lock. then reinstall and update expo
follows cmd:-
$ npm install
$ yarn add expo
$ expo update
try this , works for me.
!!enjoy!!
As the other answer suggests, once you've done console.log to see the component is actually loading, then for me the issue was I couldn't actually see the content.
My solution was to wrap my content with a <View> to align the content in the middle of the page.
I understand your question is more complex than that, but hopefully, my answer might be able to help other people.
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'space-around',
}}>
<Text>Can you see this?</Text>
</View>
in my case,
style = {{ borderColor : #fff }}
my mistake is exceptin ' at borderColor value...
fix change to
style = {{ borderColor : '#fff' }}
Some components such as useState was imported from wrong url, I changed it and imported it from react and fixed it

React Komposer + Container Pattern + Data Input

I'm now working with react-komposer and the container/component pattern, but it's left me wondering how to handle data input.
For example, an AddVehicleForm component has a container that pre-populates some fields with data from the database. With the standard React Komposer examples, this makes sense:
import { composeWithTracker } from 'react-komposer';
import { Vehicles } from '../../collections/vehicles.js';
import AddVehicleForm from '../components/AddVehicleForm.jsx';
const composer = ( props, onData ) => {
const subscription = Meteor.subscribe( 'vehicles' );
if ( subscription.ready() ) {
const curVehicles = Vehicles.find().fetch();
onData( null, { curVehicles } );
}
};
const Container = composeWithTracker( composer )( AddVehicleForm );
But, to keep the component truly unreliant on it's data source, you would also need to pass it a handleSubmit() function to submit to the database, would you not? Where would you put this function?
Alternatively, I can see how it wouldn't be hard to solve using TrackerReact. But, as React Komposer is so widely adopted, what's the common way to handle this case?
EDIT:
Just throwing out an idea, but is there any reason not to create a container component with submit handling methods and then wrap that with the composer function? Something akin to this:
import {composeWithTracker} from 'react-komposer';
import ClassroomDashboard from '/imports/components/classroomDashboard/ClassroomDashboard.jsx';
class ClassroomDashboardContainer extends React.Component {
onSubmitHandle(e) {
// check form data and submit to DB
}
render() {
return(
<ClassroomDashboard {...this.props} onSubmit={this.onSubmitHandle.bind(this)} />
)
}
}
function composerFunction(props, onData) {
const handle = Meteor.subscribe('classroom');
if (handle.ready()) {
const classroom = Classrooms.findOne(props.params.id);
onData(null, {classroom});
};
};
export default composeWithTracker(composerFunction)(ClassroomDashboardContainer);

Updating a template with a component input

Preface: I'm new to Meteor, Angular, and Typescript, so there is a very real possibility of an XY problem somewhere in here.
I'm working on a simple project management app using Meteor and Angular 2 (using the angular2-meteor package) where the structure (for now) consists of projects which have events. One view is a list of projects. Clicking on a project shows a modal of the project's details, including a list of the project's events. So, three components: ProjectList, ProjectDetails, and ProjectEventsList. ProjectDetails uses a Session variable to know which project to show, and that works. However, the list of events in the modal doesn't update after it is created for the first project clicked on.
ProjectEventsList.ts
import {Component, View} from 'angular2/core';
import {MeteorComponent} from 'angular2-meteor';
import {ProjectEvents} from 'collections/ProjectEvents';
#Component({
selector: 'projectEventsList',
inputs: ['projectId']
})
#View({
templateUrl: '/client/projectEventsList/projectEventsList.html'
})
export class ProjectEventsList extends MeteorComponent {
projectEvents: Mongo.Cursor<ProjectEvent>;
projectId: string;
constructor() {
super();
this.subscribe('projectEvents', this.projectId, () => {
this.autorun(() => {
this.projectEvents = ProjectEvents.find({projectId: this.projectId});
}, true);
});
}
}
As I understand it (though I may be way off here), I'm having difficulty getting autorun to, well, automatically run. I've tried putting a getter and setter on projectId and it does get updated when I click on a project, but the code inside autorun doesn't run after the first click. Things I've tried:
Switching the nesting of subscribe() and autorun().
Adding/removing the autobind argument to both subscribe() and autorun(). I don't really understand what that's supposed to be doing.
Moving the subscribe code to a setter on projectId:
private _projectId: string = '';
get projectId() {
return this._projectId;
}
set projectId(id: string) {
this._projectId = id;
this.subscribe('projectEvents', this._projectId, () => {
this.projectEvents = ProjectEvents.find({projectId: this._projectId});
}, true);
}
When I do this the list stops displaying any items.
If this all seems like it should work, I'll create a small test case to post, but I am hoping that something in here will be obviously wrong to those who know. Thanks!
this.subscribe() and this.autorun() doesn't seem to be part of the Angular component class. If this is an external library you might need to explicitly run it in an Angular zone for change detection to work:
constructor(private zone: NgZone) {
this.subscribe('projectEvents', this.projectId, () => {
this.autorun(() => {
zone.run(() => {
this.projectEvents = ProjectEvents.find({projectId: this.projectId});
});
}, true);
});
}
If you want to subscribe to events fired from the component itself use host-binding
#Component(
{selector: 'some-selector',
host: {'projectEvents': 'projectsEventHandler($event)'}
export class SomeComponent {
projectsEventHandler(event) {
// do something
}
}
I eventually got the setter method working, as shown below. It feels clunky, so I'm hoping there's a cleaner way to do this, but the below is working for me now (i.e., the list of events is updated when the parent component (ProjectList) sends a new projectId to the input.
ProjectEventsList.ts
import {Component, View} from 'angular2/core';
import {MeteorComponent} from 'angular2-meteor';
import {ProjectEvents} from 'collections/ProjectEvents';
#Component({
selector: 'projectEventsList',
inputs: ['projectId']
})
#View({
templateUrl: '/client/projectEventsList/projectEventsList.html'
})
export class ProjectEventsList extends MeteorComponent {
projectEvents: Mongo.Cursor<ProjectEvent>;
set projectId(id: string) {
this._projectId = id;
this.projectEventsSub = this.subscribe('projectEvents', this._projectId, () => {
this.projectEvents = ProjectEvents.find({projectId: this._projectId}, {sort: { startDate: 1 }});
}, true);
}
get projectId() {
return this._projectId;
}
constructor() {
super();
this.subscribe('projectEvents', this.projectId, () => {
this.projectEvents = ProjectEvents.find({projectId: this.projectId});
}, true);
}
}

Resources