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.
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.
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
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 use axios to get data from the api (https://reqres.in/) and display in my react app. Before this I fetched the data from the API using fetch method in javascript. Now I have tried coding this from various resources. How should I do it. Is it the correct method?
My app.js file-
import React, { Component } from 'react';
import './App.css';
import axios from 'axios';
class App extends Component {
constructor(props) {
super(props);
this.successShow = this.successShow.bind(this);
this.errorShow = this.errorShow.bind(this);
}
componentDidMount() {
axios.get('https://reqres.in/api/products/3')
.then(function (response) {
this.successShow(response);
})
.catch(function (error) {
this.errorShow(error);
});
}
successShow(response) {
this.member = <pre>{JSON.stringify(response.data, null, '\t')}</pre> ;
}
errorShow(error) {
this.member = <pre>{JSON.stringify(error.response.data, null, '\t')}</pre>;
}
render() {
return (
<div>
<h2>Welcome to React</h2>
<h3>{JSON.stringify(this.state.person.data)}</h3>
<div>{this.member}</div>
</div>
);
}
}
export default App;
It also gives the error - Unhandled Rejection (TypeError): Cannot read property 'errorShow' of undefined.
Changes:
1. You need to bind this with then and catch callback methods, use arrow functions.
2. You didn't define the initial state and using this.state.person.data it will throw error.
3. Storing the UI in state or global variable is not a good idea, ui part should be inside render method only.
Write it like this:
class App extends Component {
constructor(props) {
super(props);
this.state = {
person: {}
}
//this.successShow = this.successShow.bind(this);
//this.errorShow = this.errorShow.bind(this);
}
componentDidMount() {
axios.get('https://reqres.in/api/products/3')
.then((response) => {
this.successShow(response);
})
.catch((error) => {
this.successShow(error);
});
}
successShow(response) {
this.setState({
person: response.data
});
}
render() {
return (
<div>
<h2>Welcome to React</h2>
<h3>{JSON.stringify(this.state.person.data)}</h3>
<pre>{JSON.stringify(this.state.person.data)}</pre>
<div>{this.member}</div>
</div>
);
}
}
When you call this.errorShow() inside of the function, this is not your component object, but context of function. You should use arrow functions instead, arrow functions do not create its own this so you can access your component this:
componentDidMount() {
axios.get('https://reqres.in/api/products/3')
.then((response) => {
this.successShow(response);
})
.catch(error) => {
this.errorShow(error);
});
}
More info about arrow functions
Try this:
componentDidMount() {
axios.get('https://reqres.in/api/products/3')
.then((response) => {
this.successShow(response);
})
.catch((error) => {
this.errorShow(error);
});
}
Use arrow functions to remain the right scope of this
The problem is that the this in your then and catch callbacks doesn't refer to your class, but to the default (global) scope. You need to bind the right this. You actually already have the appropriate functions set up with this binding, so you can just use them directly:
componentDidMount() {
axios.get('https://reqres.in/api/products/3')
.then(this.successShow)
.catch(this.errorShow);
}
In general, you can also use => function syntax, which inherits the 'this' from the scope the function is declared in, rather than using the global scope. E.g.
componentDidMount() {
axios.get('https://reqres.in/api/products/3')
.then(success => this.successShow(success))
.catch(error => this.errorShow(error));
}
(note the => functions are completely unnecessary here of course).
You have an additional problem, which is the you need to store member in component state (this.state.member), not just as a field, and use the setState function to update it. Otherwise your component won't re-render when you update member.
I am using React v0.14.8. I tried to call the fetchData function from another component. Here is my code:
export default class TagUtils extends React.Component {
deleteTag = () => {
Tags.deleteTag(this.props.tag).then(function(response){
if(response.message === 'tag successfully deleted')
Sidebar.fetchData();
});
}
// other codes
And:
export default class Sidebar extends React.Component {
fetchData() {
Tags.getTags().done((response) => {
this.setState({tags: response.tags || [], loaded: true});
});
}
//other codes
When I called deleteTag, I got this error in my console:
TypeError: _SidebarJsx2.default.fetchData is not a function
You can't call Sidebar.fetchData because fetchData is not a static member of Sidebar, it is an instance member. This means you need an instance of Sidebar to call fetchData on, for example new Sidebar().fetchData(). Of course, this is not how a React component is supposed to be used, and it would not set state on all other Sidebar instances, so it wouldn't be meaningful.
What you want to do is pass a callback to your TagUtils component:
export default class TagUtils extends React.Component {
deleteTag = () => {
Tags.deleteTag(this.props.tag).then((response) => {
if(response.message === 'tag successfully deleted')
this.props.onDeleteTag();
});
}
}
export default class Sidebar extends React.Component {
fetchData() {
Tags.getTags().done((response) => {
this.setState({tags: response.tags || [], loaded: true});
});
}
render() {
return (
{ this.state.tags.map((tag) =>
<TagUtils tag={tag} onDeleteTag={this.fetchData} />) }
);
}
}
If you have to thread this callback through several layers of components that's okay, that's typical in React. However, if you find yourself passing a lot of stuff down props through many component layers that seem out of place, or trying to reconcile changes across large horizontal spaces in your app, this is a primary use-case for things like Flux and Redux.