In my App component I call checkUserAuth() method in order check if user is logged in or not and render different content according to this. It works fine when I call it via componentDidMount() method however it doesn't work If I try to call it via another method:
TypeError: this.checkUserAuth is not a function
In my code:
class App extends React.Component {
constructor() {
super();
this.state = {
loggedIn: false
};
this.checkUserAuth = this.checkUserAuth.bind(this);
}
componentDidMount(){
this.checkUserAuth(); // here this method can be called
}
checkUserAuth(){
const loggedUser = AuthService.isAuthenticated();
if(loggedUser){
store.dispatch(actions.loginSuccess());
this.setState({loggedIn: true});
}
}
logoutUser(){
store.dispatch(actions.logout());
this.checkUserAuth(); // here it return errors
}
How can I fix it?
put this in the constructor:
this.logoutUser = this.logoutUser.bind(this);
Also, consider using an auto-binding library if you find it tedious to always bind stuff. I use react-autobind
Related
Could you please have a look on the following code. I need to get some value from another class. This works asynchronous, so I provided a function handleGameDeserialization.
The function gets the right value (as I tested with the alert), however the setState function has no impact. Could that be a "this-context" issue?
export default class GameplayScreen extends React.Component {
constructor(props) {
super(props);
this.fbGame = new FBGame();
global.currentScreenIndex = 'Gameplay';
this.state = {
currentGame: 'N/A'
}
// this.handleGameDeserialization = this.handleGameDeserialization.bind(this);
if (this.props.route.params != null) {
this.gameKey = this.props.route.params.gameKey;
this.game = this.fbGame.deserializeGame(this.gameKey, this.handleGameDeserialization);
}
}
handleGameDeserialization = (game) => {
// alert('yeah'+game); // here comes the expected output
this.setState({
currentGame: game
});
}
render() {
return (
<View>
<Text>{this.state.currentGame}</Text>
</View>
/*<Board game={this.state.game}/>*/
)
}
}
I call that function when the component GameplayScreen is navigated to. As you can see above, there is a class FBGame, which does the deserialization (read the game from firebase database)
export default class FBGame {
...
deserializeGame(key, handleGameDeserialization) {
var gameRef = firebase.database().ref("games/"+key).child("serialized");
gameRef.on("value", snapshot => {
//console.log('deserialized: ' + );
handleGameDeserialization(snapshot.val().serialized);
});
}
...
}
edit:
When I use componentDidMount like below, it works fine. But this seems to be an anti-pattern. I still don't understand, why it doesn't work, when callded in the constructor and how I am supposed to solve this.
componentDidMount() {
this.game = this.fbGame.deserializeGame(this.gameKey, this.handleGameDeserialization);
}
For things like subscriptions that will update the state and other side-effects, you should put the logic out in componentDidMount() which will fire immediately after the component is mounted and won’t give you any trouble if you update the state inside of it.
You can't but things that call this.setState in the constructor.
Whenever setState() is called, the component doesn't seem to rerender. As you can see by my comments, the state does in fact change and render seems to be called again, but if I don't add that if statement and simply add a paragraph tag that displays the data it will give me an error. I'm sure I'm missing something simple, but any help is appreciated.
import React from "react";
import axios from "axios";
import { constants } from "../constants/constants";
const { baseURL, apiKey, userName } = constants;
class User extends React.Component {
constructor(props) {
super(props);
this.state = {
user: []
};
}
componentDidMount() {
let getUserInfo = axios.create({
baseURL,
url: `?
method=user.getinfo&user=${userName}&api_key=${apiKey}&format=json`
});
getUserInfo().then(response => {
let data = response.data;
console.log(data.user.playcount); //logs second, displays correct
this.setState(state => ({
user: data
}));
});
}
render() {
console.log(this.state); //logs first and third, doesn't work on first but does on third
let toReturn;
if (this.state.user.length > 0) {
toReturn = <p>{this.state.user.user.playcount}</p>;
} else {
toReturn = <p>didn't work</p>;
}
return <div>{toReturn}</div>;
}
}
export default User;
React LifeCycle function sequence is Constructor and then it calls render method.
In constructor method it initialises the state which is currently empty user array.
Now it calls render() method as this.state.user is an empty array, referencing something out of it gives an error
this.state.user.user.playcount
this will generate an error if you dont have if condition.
After the first render it will call componentDidMount, now you fetch something update state. As setState occurred, render will be called again Now you have something in this.state.user then displaying will happen.
this.state.user.length > 0 is true
Look at this: https://reactjs.org/docs/react-component.html and https://reactjs.org/docs/conditional-rendering.html
You can right in single tag using conditional render like this
<p>{this.state.user.length ? this.state.user.user.playcount : 'loading'}
Hope this helps.
I think your problem might have something to do with the changing shape of the user value. You initialise the value to an empty array, but then—after the fetch is done—you assume it's an object (by using user.user).
Maybe you could simplify the code a bit to look more like the one below?
/* imports */
class User extends React.Component {
constructor(props) {
super(props);
this.state = {
user: null // Make it explicit there's no value at the beginning.
};
}
componentDidMount() {
let getUserInfo = axios.create(/* ... */);
getUserInfo().then(response => {
let data = response.data;
this.setState({ // No need to for a setter function as you dno't rely on the previous state's value.
user: data.user // Assign the user object as the new value.
});
});
}
render() {
let toReturn;
// Since it's now a `null`, you can use a simple existence check.
if (this.state.user) {
// User is now an object, so you can safely refer to its properties.
toReturn = <p>{this.state.user.playcount}</p>;
} else {
toReturn = <p>No data yet.</p>;
}
return <div>{toReturn}</div>;
}
}
export default User;
I am trying to call a function from componentDidMount which sets the State but keep coming across an error of
Uncaught ReferenceError: setPanelState is not defined
Below is the code...
export default class Patient extends React.Component {
constructor(props) {
super(props);
autoBind(this);
this.state = {
PATIENT: [],
COMPPROPS: [],
};
this.setPanelState = this.setPanelState.bind(this);
}
setPanelState(activity) {
this.setState({COMPPROPS: [{compName:'Overview', compState:'Edit'}]});
}
componentDidMount() {
//handles chat commands and if the command is update patient the Overview panel should change to editable
this.directLine.activity$
.filter(function (activity) {
return activity.type === 'event' && activity.value === 'Update Patient';
})
.subscribe(function (activity) {
setPanelState(activity);
})
}
I have tried make setPanelState a function outside of the class as opposed to a method but I get an error there as well.
Any thoughts?
Since you're using ES6 classes I assume you have it all set up.
Use arrow functions that bind this automatically
To learn more about arrow functions see this
.subscribe((activity) => {
this.setPanelState(activity);
})
Your component would look like this:
export default class Patient extends React.Component {
constructor(props) {
super(props);
autoBind(this);
this.state = {
PATIENT: [],
COMPPROPS: [],
};
this.setPanelState = this.setPanelState.bind(this);
}
setPanelState(activity) {
this.setState({COMPPROPS: [{compName:'Overview', compState:'Edit'}]});
}
componentDidMount() {
//handles chat commands and if the command is update patient the Overview panel should change to editable
this.directLine.activity$
.filter((activity) => {
return activity.type === 'event' && activity.value === 'Update Patient';
})
.subscribe((activity) => {
this.setPanelState(activity);
})
}
Use this.setPanelState(activity) and remember to maintian the context .As you are non ES6 arraow function . save the context outside var that=this and access that variable inside
In your componentDidMount method call setPanelState using this.setPanelState
you can also use a better format:
.subscribe(this.setPanelState)
If you put setPanelState outside the class and call it, it won't work,
unless it is defined inside another class where you can use setState.
change to
this.setPanelState(activity)
at the end.
Imagine I have some "page" component, which needs to ask for data from a server. The data it requests will depend on whether or not the current user is authenticated. Further, in the event of a login, the page will want to reload the data. My question is, how can I accomplish something like this using HOCs rather than inheritance?
To illustrate the problem, I'll demonstrate a solution using inheritance. The program will have the following objects. I'll leave out the boilerplate code.
session: an EventEmitter that emits start when the session changes (either a login or a log out).
Page: the superclass that all pages inherit from
MyPage: the subclass of Page in this example
API: will be an API class for retrieving data from the server
Here's the code:
// Page superclass
class Page extends React.Component {
componentWillMount() {
session.on("start", this.loadData);
this.loadData();
}
loadData() {
// this method is overwritten in subclasses
}
}
// MyPage subclass
class MyPage extends Page {
loadData() {
if(session.isAuthenticated()) {
API.loadPrivateData();
} else {
API.loadPublicData();
}
}
}
Here's a solution that uses an HOC, but seems less elegant than inheritance. It still requires that every "subclass" page have a method loadData, and it requires that method to be called in every "subclass's" componentWillMount.
// Page HOC
function Page(WrappedComponent) {
return class EnhancedPage extends React.Component {
componentWillMount() {
session.on("start", this.loadData);
// this._page.loadData() will fail here
// since this._page is undefined until rendering finishes
}
loadData() {
this._page.loadData();
}
render() {
return <WrappedComponent {...props} ref={(e) => { this._page = e; }} />
}
}
}
// MyPage
class MyPage extends React.Component {
componentWillMount() {
this.loadData();
}
loadData() {
if(session.isAuthenticated()) {
API.loadPrivateData();
} else {
API.loadPublicData();
}
}
}
const component = Page(MyPage)
// what would make sense here is to have a method something like
// const component = Page(MyPage, () => MyPage.loadData())
// but then the MyPage.loadData logic would need to be defined
// elsewhere
This pattern will happen often: I'll want to load some data, then reload when the session changes. I'd like to understand the "react" way of accomplishing the same.
EDIT: I am not trying to pass a username or "loggedIn" flag through the HOC. That is to say something like <WrappedComponent isLoggedIn={session.isAuthenticated()} {...props} /> won't cut it here. Tying the API logic to props requires that I check for changes in MyPage.componentWillUpdate().
When using a HOC you shouldn't place the loadData function on the wrapped component. Instead pass the function as a parameter to the HOC constructor.
Something like this might work for you. The sessionHoc function takes a callback function which'll be called every time the session state changes. Its result will be passed to WrappedComponent as a data prop.
function sessionHoc(onSessionChange) {
return function (WrappedComponent) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null,
};
session.on('start', this.handleSessionChange.bind(this));
}
handleSessionChange() {
this.setState({
data: onSessionChange(),
});
}
render() {
return <WrappedComponent data={data} {...this.props} />
}
};
};
}
class MyPage extends React.Component {
render() {
// Just access this.props.data here!
}
}
const EnhancedPage = sessionHoc(function () {
if (session.isAuthenticated()) {
return API.loadPrivateData();
} else {
return API.loadPublicData();
}
})(MyPage);
Hopefully this helped! :)
I'm using React + Flux on the frontend for a project and I need to get the username to display it on the sidebar.
The problem: I call the action in the constructor of the es6 class which fetches the data needed from the API, and I can see it being logged to the console from the onUserStateChanged method, but it doesn't get updated in the state within the render method. I don't understand how I can get the state change reflected in the component.
export default class extends React.Component {
constructor() {
super();
UserActions.getCurrentUser();
this.state = {
user: {}
};
}
componentDidMount() {
UserStore.addChangeListener(this.onUserStateChange);
}
componentWillUnmount() {
UserStore.removeChangeListener(this.onUserStateChange);
}
onUserStateChange() {
console.log('called');
this.state = {
user: UserStore.getCurrentUser()
};
console.log(this.state);
}
render(){
var _this = this;
return (
console.log(this.state);
<div>{_this.state.user.username}</div>
);
}
}
The call to console.log from onUserStateChange() contains the correct state back from the API whereas the console.log in render just shows a blank JS object
You probably want to use setState
As documentation says:
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
Also your constructor seems strange, do you really intend to not use the result of UserActions.getCurrentUser(); in
UserActions.getCurrentUser();
this.state = {
user: {}
};