2

I just started to learn React. I want to use my already built REST api but I've encountered a problem while I was trying to get an user element from my api.

I have a UserServices class that has two axios methods in it, getUserById and getAllUsers.

public static getUserById(id: number): Promise<IUser> {
    return new Promise((resolve, reject) => {
        axios.get(this.root + id).then((response: any) => {
            let character = this.toUser(response.data);
            resolve(character);
        },
            (error: any) => {
                reject(error);
            });
    });
}

enter image description here

My problem is when I try to render my user, because I get nothing.

class App extends React.Component {
  user: IUser;
  render() {
    UsersService.getUserById(2).then(res => this.user = res);

    return (
        <div className="app">
          <User user = {this.user} />
        </div>
    );
  }
}

This is my user component

export interface UserProps {
    user: IUser;
}

interface UserState {
    myUser: IUser;
}

export class User extends React.Component<UserProps, UserState> {

    public static defaultProps: UserProps = {
        user: {
            name: ''
        }
    }

    constructor(props: UserProps) {
        super(props);

        this.state = {
            myUser: props.user
        }
    }

    render() {
        return (
            <div>
                <span>{this.state.myUser.name}</span>
            </div>
        );
    }
}
Darius Pintilie
  • 112
  • 1
  • 8
  • `this.user` will still be `undefined` when you return the div containing it in `App#render`. See the linked dupetarget for why. The promise is resolved **later**. You should be 1. Retrieving the user earlier, 2. Showing a "loading" message while the user is being loaded, and 3. Using `setState` within the `then` callback to change the state when you have the user (which will cause a new rendering). – T.J. Crowder Dec 13 '17 at 18:43
  • 1
    @T.J.Crowder This is not a duplicate of that question. – Blue Dec 13 '17 at 18:44
  • @FrankerZ: Yes, it is. Have a read. Couldn't be much more clearly trying to use an asynchronous result prior to it arriving. – T.J. Crowder Dec 13 '17 at 18:54
  • @T.J.Crowder This is a fault of the state not being reread again to trigger a re-render. While that answer mentions the asynchronous nature of the render method just returning, I think this question is more directly related to why the app isn't re-rendered when the user is then resolved. – Blue Dec 13 '17 at 18:59
  • @T.J.Crowder it is not related to the question you marked as dup – Cleiton Dec 13 '17 at 18:59
  • @Cleiton: Again: It couldn't be more clearly trying to use an asynchronous result prior to it arriving. This is the canonical dupe for that situation. – T.J. Crowder Dec 13 '17 at 19:00
  • @T.J.Crowder A cannocal dupe would be `this.state.user = UsersService.getUserById(2)` or similar. I've nominated the question for reopening, as I feel this is more related to react/rendering. – Blue Dec 13 '17 at 19:02
  • But I think it is more a React related question, as the user should know how to deal with async code in a opinionated way. – Cleiton Dec 13 '17 at 19:02
  • @Cleiton: So [this](https://stackoverflow.com/questions/33221697/how-to-re-render-reactjs-component-after-update-in-data) or [this](https://stackoverflow.com/questions/39188002/why-isnt-the-state-of-root-component-changing) or [this](https://stackoverflow.com/questions/36183304/change-component-state-on-button-click/36184039#36184039)? I've added the one that seems like the dupetarget for the other two. – T.J. Crowder Dec 13 '17 at 19:10
  • 1
    @T.J.Crowder all are dups, but the last one has the best answer of all. – Cleiton Dec 13 '17 at 19:13
  • @Cleiton: Yeah, agreed. I wish it were the earlier one, but you're right it's got the clearly better answer. – T.J. Crowder Dec 13 '17 at 19:15

1 Answers1

1

For anything variable, you either need to use state management, so that react can detect a change to this.state.user, and rerendering the appropriate components.

EDIT: As Cleiton mentioned, componentDidMount() would be a better place to put the call.

Set your state as well, so that your app will rerender when state changes. In either component be sure to check if User is null, and display some type of loading in the render method, or return null (To render nothing) in the case of the user hasn't been loaded yet.

class App extends React.Component {
  constructor() {
      super();

      this.state = {
          user: null
      }
  }

  componentDidMount() {
    UsersService.getUserById(2).then(res => this.setState({user: res}));
  }

  render() {

    return (
        <div className="app">
          <User user={this.state.user} />
        </div>
    );
  }
}
Blue
  • 22,608
  • 7
  • 62
  • 92
  • 1
    `render` method should be pure, you should not be doing anything remotely related to modifying any component's state within a render function. Tell him to use `componentDidMount` instead. – Cleiton Dec 13 '17 at 18:54
  • `User` will throw an exception with the above unless you also modify `User` (or modify the `render` above not to use `User` when `this.state.user` is `null`). – T.J. Crowder Dec 13 '17 at 18:57