COnsider the following:
componentDidMount(){
this.props.requestUser(this.props.match.params.userID).then(res =>
{
this.pageOwner = res.user;
debugger;
})
}
Why is my this.pageOwner still returning undefined? I have been trying my as off to get a user into a state that my user profile can actually acces.
If you're trying to access this.pageOwner, you need to do so after the this.props.requestUser() async function has completed. You're currently getting the user in componentDidMount(), so your render() will initially see this.pageOwner as undefined until the async call is finished.
Additionally, it might be better to store the result of your async call in state so that your component will re-render once the value is filled.
As #colin said, put it in this.state. If you are getting an error in the render() function, then just make your render like so:
constructor() {
super();
this.state = {
pageOwner: null
}
}
componentDidMount() {
this.props.requestUser(this.props.match.params.userID).then(res =>
{
this.setState({pageOwner: res.user});
debugger;
})
}
render() {
if(this.state.pageOwner)
return ( ... )
else
return null;
}
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 can call data in console.log(this.state.eventUser); render return and its showing all of the data in eventUser. But when I try to call console.log(this.state.eventUser._id); its showing error of this Uncaught TypeError: Cannot read property '_id' of undefined. How can I solve this issue?
componentDidMount(){
Tracker.autorun(() => {
Meteor.subscribe('allUsers');
const userId = this.props.location.state.event.userID;
const eventUser = Meteor.users.findOne({_id: userId});
this.setState({ eventUser });
});
}
render(){
return(
<div>
{console.log(this.state.eventUser._id)}
</div>
);
}
Probably at some point this.state.eventUser in the render is undefined.
Try this
{this.state.eventUser && console.log(this.state.eventUser._id)
You can't use console.log inside JSX without enclosing in {}
Try below code
render(){
return(
<div>
{put javascript here}
</div>
);
}
Edit:
Your code is executing query on Database which is asynchronous
So you need to wait for it to complete.
Below is an implementation of async/await (es7+ features of javascript)
Try below code
componentDidMount(){
Tracker.autorun(async () => {
Meteor.subscribe('allUsers');
const userId = this.props.location.state.event.userID;
const eventUser = await Meteor.users.findOne({_id: userId});
this.setState({ eventUser });
});
}
It looks like by the time your render() function runs, the user hasn't yet be saved to the component state.
To fix this, add a constructor to your component in which you define the eventUser as an empty object.
class MyClass extends Component {
constructor(props) {
super(props);
this.state {
eventUser: {}
};
}
componentDidMount() {
Tracker.autorun(() => {
Meteor.subscribe('allUsers');
const userId = this.props.location.state.event.userID;
const eventUser = Meteor.users.findOne({_id: userId});
this.setState({ eventUser });
});
}
render(){
console.log(this.state.eventUser._id)
return(
<div>My Test Page</div>
);
}
}
Hope that helps!
componentDidMount will fire after the component has already been mounted (as the name states). At the point where it reaches the enclosed statement the state has not been set yet.
I'm not sure why you are using a console.log inside of your render return, but if you are looking to actually display the user ID, conditional rendering is your friend: {this.state.eventUser && this.state.eventUser._id}
For the first time when your component will render, if you have knowledge about react lifecycle hooks, after constructor the render method will be triggered then componentDidMount hook.
so for the first render the eventUser is undefined then after componentDidMount the state will be fulfilled.
So solution is:
First don't put console.log in JSX, it is better to put it before return.
Second first check if the object is defined then return your data
Code:
render(){
const { eventUser } = this.state;
// if eventUser is defined then log it
{eventUser && console.log(eventUser._id)}
// or you can add very simple load state
if(!eventUser) return <h1>Loading....</h1>
return(
<div>
{probably info about user }
</div>
);
}
In the React component's componentDidMount() I make an axios get request to receive a response and setState to the component. The response is correct, and when print out the component object in the component class with this, the object looks good. Then I call console.log(this.state), then every property of the component become empty. Why this happens? How can I get the state's property?
MyComponent.js
React component did mount method:
componentDidMount() {
getLastWeek(this); // here I make a get request
console.log('===');
console.log(this); // this line prints out an object will all the properties
console.log(this.state); // all properties of state disappear
}
The get request used above:
service.js
...
function getLastWeek(component) {
const lastWeek = getEndpoint(7);
Axios.get(lastWeek)
.then(res => {
const bpi = res.data.bpi;
const prices = Object.values(bpi);
component.setState({ lastWeek: prices });
})
.catch(err => {
console.log(err);
});
}
...
You are making an axios request which is an asynchronous function, so what is happening is you are using console.log(this.state) before the state gets set.
The render() method gets executed every time the state changes so if you put your console.log inside the render() method you should now see how your state change. Something like this:
class Example extends Component {
constructor() {
...
}
componentDidMount() {
...
}
render() {
console.log(this.state);
return(...);
}
}
I am calling an asynchronous function, and based off that response, the function should be returning a different React Native Element.
I have written simpler code that creates the same error.
async function doNothing(){ };
class RouterComponent extends Component {
render() {
return(
doNothing().then(()=> {
return(
<View></View>
);
})
The error I am getting is
A Valid React element or (null) must be returned. You may have returned undefined, an array, or some other invalid object.
I am returning a valid React element within the .then(statement). How would I change it so it returns the React Element.
If you want to perform async operation in the same file, then move async call to some lifecycle method like ComponentDidMount. Define a state object in constructor,
constructor(props) {
super(props);
this.state = {ComponentToRender: ''}
}
Trigger network call from you componentDidMount method,
componentDidMount() {
doNothing().then(()=> {
this.setState({
ComponentToRender: <ComponentToRenderBasedOnResponse>
})
})
}
Your render method should use this.state.ComponentToRender method to decide, which component to render.
render() {
if(this.state.ComponentToRender === 'A') {
return this.renderComponentA();
}
else {
return null;
}
}
You can decide, how you want to organize your render method.
In this case render function is not a asncy function. so render function would not wait for your doNothing finishes. So it returns null. You can probably try something like this.
constructor(props) {
super(props);
this.state = { renderA: false, renderB: false }
}
componentDidMount() {
doNothing().then((res)=> {
if (res.Something){
this.setState({ renderA: true })
}
});
}
renderA() {
return (<View> view for A </View>);
}
renderB(){
return (<View> view for B </View>);
}
render() {
this.state.renderA ? this.renderA() : null;
this.state.renderB ? this.renderB() : null;
}