How to change the behavior of Meteor accounts-ui so when a user logs in, the log out form automatically appears and vice versa - meteor

When a user logs in, I've tried adding a class to the 'logout form' that has a 'display: block' even with the '!important' tag which would override any display property on the logout form. I've tried reloading the page because that does bring up the logout form once a user logs in but it gets stuck in an infinite loop.
import React from 'react';
import ReactDOM from 'react-dom';
export default class AccountsUI extends React.Component {
componentDidMount() {
Accounts._loginButtonsSession.set('dropdownVisible', true);
this.view = Blaze.render(Template.loginButtons,
ReactDOM.findDOMNode(this.refs.container));
setTimeout(function () {
window.requestAnimationFrame(function() {
var node = ReactDOM.findDOMNode();
if (node !== undefined){
Accounts.onLogin(function(user){
document.getElementById('login-dropdown-list').className = "accounts-dialog hide-div"
console.log(document.getElementById('login-dropdown-list').className)
})
}
});
}, 250)
}
componentWillUnmount() {
Blaze.remove(this.view);
}
render() {
return <span ref="container" />
}
}
I'm also going to change how the class additions are triggered. I know that waiting 1/4 a second is very primitive and won't always work.

The Meteor.userId() function is reactive, which means if you call it in getMeteorData() it will be called again each time the userId changes. Save it to this.data, and use it in render().
I'd also suggest you create a React wrapper called LogoutUIWrapper for the Blaze component that only does wrapping and nothing else, just to make your life easier. See here: https://www.meteor.com/tutorials/react/adding-user-accounts
So you'll need to do something like this:
export default class AccountsUI extends React.Component {
getMeteorData() {
return {
userId: Meteor.userId(),
};
}
render() {
return (
<div>
{ this.data.userId ? null : <LogoutUIWrapper /> }
</div>
);
}
}
This way the LogoutUIWrapper component will only appear when the user is logged in.

Related

react-native navigating between screens from non component class

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
});

REDUX: Why won't the store provide data to my component?

Newbie here trying to learn some Redux.
GOAL: to get a button to click and login/logout, updating the store as true/false status whichever way.
const store = createStore(myReducer)
Created my store, passing in my reducer.
This has a default state of logged out. And returns the opposite, whenever the button is clicked.
I know this action works through debugging.
function myReducer(state = { isLoggedIn: false }, action) {
switch (action.type) {
case 'TOGGLE':
return {
isLoggedIn: !state.isLoggedIn
}
default:
return state
}
}
The problem starts here - when i try to access the store.getState() data.
class Main extends React.Component {
render() {
return (
<div>
<h1>Login Status: { state.isLoggedIn }</h1>
<button onClick={this.props.login}>Login</button>
</div>
)
}
}
const render = () => {
ReactDOM.render(<Main status={store.getState().isLoggedIn} login={() => store.dispatch({ type: 'TOGGLE' })}/>, document.getElementById('root'));
}
store.subscribe(render);
render();
I've tried store.getState().isLoggedIn & store.getState() & this.props.status and then assigning the store.getState().isLoggedIn in the Main component - but nothing works.
Can anyone tell me where i'm going wrong?
You don't directly access the store using getState to find data. The Redux docs explain the process in-depth, but basically you'll connect each component to the Redux store using connect method of the react-redux package.
Here's an example of how this could work for your above component:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import Main from '../components/Main'
class MainContainer extends Component {
render() {
return <Main {...this.props} />
}
}
const mapStateToProps = state => ({
isLoggedIn: state.isLoggedIn,
})
const mapDispatchToProps = dispatch => ({
login() {
dispatch({type: 'TOGGLE'})
},
})
MainContainer = connect(
mapStateToProps,
mapDispatchToProps,
)(MainContainer)
export default MainContainer
You would then want to render the MainContainer in place of the Main component. The container will pass down isLoggedIn and login as props to Main when it renders it.

How to get Accounts.onLogin to impact my app in Meteor React ES6?

I want my meteor app to call setState in App on login and logout. How can I have one section of code (ie: Accounts.onLogon) affect inside another component (ie App{})? Also, what to do to detect logouts?
Accounts.onLogin(function(user){
console.log('hi');
//App.showPrivate();
});
class App extends Component {
constructor(props) {
super(props);
this.state = {
showPublic: false,
};
}
toggleShowPublic() {
this.setState({
showPublic: !this.state.showPublic,
});
}
showPublic() {
this.setState({
showPublic: true,
});
}
showPrivate() {
this.setState({
showPublic: false,
});
}
render() {
return (
<div className="container">
<div className="show-public" onClick={this.toggleShowPublic.bind(this)}>
{this.state.showPublic ?
<span className="private-public"> View private</span> :
<span className="private-public"> View public </span>
}
</div>
</div>
);
}
}
Instead of Accounts.onLogin you should use Meteor's in-built reactive data sources to determine the user's logged-in status:
class App extends Component {
constructor(props) {
super(props);
this.state = { showPublic: false };
}
toggleShowPublic() {
this.setState({ showPublic: !this.state.showPublic });
}
render() {
return (
<div className="container">
{this.props.isLoggedIn ?
<div className="show-public" onClick={this.toggleShowPublic.bind(this)}>
{showPrivate ?
<span className="private-public"> View private</span> :
<span className="private-public"> View public </span>
}
</div> :
Show something else if the user is not logged in here...
}
</div>
);
}
}
export default createContainer(() => {
return {
isLoggedIn: !!Meteor.user()
}
}, App);
Now Meteor will take care of reactively updating this.props.isLoggedIn for you. Note that you need to install meteor/react-meteor-data and import createContainer for this to work:
import { createContainer } from 'meteor/react-meteor-data';
If you still need to do something when the user logs in, you can place Accounts.onLogin basically anywhere you want in your app, as long as you consider whether you want it to run server-side or client-side or both. For best practices regarding application structure, check out Meteor Guide.
It turns out Accounts.onLogin is a distraction. To have the app update when the user logs in or out, we need to see when the logged in user changes, and react accordingly. Seeing when something changes in React is done using componentWillReceiveProps, as seen below:
componentWillReceiveProps(nextProps) {
// user just logged in/out
if (!this.props.currentUser && nextProps.currentUser) {
this.setState({ showPublic: false });
}
}
oh, and current users comes from:
export default createContainer(() => {
return {
currentUser: Meteor.user(),
};
}, App);

Strange behavior/loading patterns when using React + Meteor pre-filling forms

Cliffs:
I'm trying to pre-fill forms from a user's previous entries, which is all stored in a MongoDB colelction I do this doing conventional Javascript:
componentDidMount(){
let name = document.getElementById("name");
name.value = this.props.iData.name;
}
This works fine unless I refresh the page, in which case I get an error that this.props.iData is undefined. So, whenever I visit the page with the pre-filled data, it works fine and subscription works fine. But when I refresh that same page, the subscription doesn't load fast enough.
The subscription is done like this:
export default createContainer(function(){
const subHandle = Meteor.subscribe("iData", function(){
});
const isLoading = !subHandle.ready();
return {
isLoading,
iData: Poll.find().fetch(),
}
}, UserSettings)
I must be doing something wrong for this to happen the way it's happening.
Did you make sure that your data is ready when componentDidMount is getting called. And also you shouldnt use document.getElementById() in react.
In react you need to use refs.
class Sample extends React.Component {
componentDidMount() {
this.ref.input.value = 'some value';
}
render() {
return (
<input ref="input" />
);
}
}
You can also do this:
class Sample extends React.Component {
render() {
return (
<div>
{!this.props.isLoading ? (
<input value = {this.props.someValue}/>
) : (
<p>Loading </p>
)}
</div>
);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

meteorjs reactjs action after authentification

I have the following react component that passed data to another component:
export default class App extends TrackerReact(Component){
getUserFrameData(){
return (FrameCollection.find().fetch());
}
render(){
return(
<div className="main-container">
<Frames
data={this.getUserFrameData()}
/>
</div>
);
}
}
Now I want my frames component to do an action when the component initialises.
export default class Frames extends Component{
componentDidMount(){
console.log(this.props.data);
}
render() {...}
}
But on I only get empty data at on loadup. I think it's because I'm using subscriptions and a login system. So how can I tell my Frames component to wait until everything is "loaded up"?
Use the ready method of the subscription object.
constructor() {
super();
this.state = {
sub: Meteor.subscribe('myPublication')
}
}
render() {
if (!this.state.sub.ready()) return <p>Loading...</p>;
return ...
}
http://docs.meteor.com/api/pubsub.html#Subscription-ready

Resources