-4

I've designed the following class.

export default class DataView extends React.Component {

  private message = "---";
  private url = ...;
  private body = { ... };
  private headers = { ... };

  constructor(props: any) {
    super(props);
    this.message = "executing...";

    ajax.post(this.url, this.body, this.headers)
      .subscribe(
        result => {
          this.message = "code " + result.status;
          console.log(result.status);
        },
        error => {
          this.message = "Problem: " + error.status;
          console.log("Problem: " + error.status)
        });
  }

  render() {
    return (
      <div style={divStyle}>
        Message: {this.message}
      </div>);
  }
}

The expectation was that the original string would be replaced with the initial message (which happens) and then, the result of the call (which doesn't happen). I'm new to React and not sure what's wrong here. This is the approach I'm using in Angular but, of course, things work a bit differently in React.

The call itself is a success and I can see the correct results in the console. It's only the binding between the class variable and the rendered value that's stale. I've been told that the rendering's done automagically and I should not call render() myself.

DonkeyBanana
  • 3,266
  • 4
  • 26
  • 65
  • 3
    I suggest stepping back from your current task and working through some [basic React tutorials](https://reactjs.org/tutorial/tutorial.html). – T.J. Crowder Sep 22 '18 at 22:58
  • 1
    @T.J.Crowder I'm doing those too. I just hope that referencing Angular (both the similarities and differences) will help me move faster, since I'm learning-by-erroring kind of individual. I might be mistaken, though. The suggestions on *componentWillMount* etc. are very helpful because that's the ammo I was lacking to shoot google with. Thanks. I saw something about the default variable *state* - I'll use that and see how it works out. – DonkeyBanana Sep 22 '18 at 23:15

2 Answers2

2

React doesn't work like that. It only triggers a new render if you change your state, which you do via setState. Your message property isn't state, and you're not changing it via setState.

Also, triggering ajax from the constructor isn't how you'd do that in React. Instead, you'd do it in componentDidMount, and you'd allow for the fact that the ajax call won't always be complete in render. (If that's not acceptable, then you push the ajax call up a level and have the parent only render this component when the data is available.)

Here's an example with minimal modifications and setTimeout standing in for ajax; see *** comments:

class DataView extends React.Component {

  /* These would all probably be state properties
  private message = "---";
  private url = ...;
  private body = { ... };
  private headers = { ... };
  */

  constructor(props) {
    super(props);
    this.state = {                                  // *** Put `message`
        message: "executing..."                     // *** on `state`
    };
  }
  
  componentDidMount() {                             // *** Use `componentDidMount`
    /*                                              // *** to start async load
    ajax.post(this.url, this.body, this.headers)
      .subscribe(
        result => {
          this.message = "code " + result.status;
          console.log(result.status);
        },
        error => {
          this.message = "Problem: " + error.status;
          console.log("Problem: " + error.status)
        });
    */
    setTimeout(() => {
        this.setState({                             // *** Update state with
            message: "code GOTSTATE"                // *** `setState`
        });
    }, 800);
  }

  render() {
    return (
      // *** No idea where `divStyle` was meant to come from, left it out below
      // Note using `this.state.message` below
      <div>
        Message: {this.state.message}
      </div>);
  }
}


ReactDOM.render(
  <DataView />,
  document.getElementById("root")
);
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

You should use state if you want rerender the datas after ajax call. Check the state and lifecycle tutorials. Your code should be like that:

export default class DataView extends React.Component {

  constructor(props) {
    super(props);
    this.state = {message: 'executing...', body: '', headers: '', 'url': ''}
  }

  componentWillMount() {
      ajax.post(this.state.url, this.state.body, this.state.headers)
          .subscribe(
            result => {
              this.setState({ message: "code " + result.status });
              console.log(result.status);
            },
            error => {
              this.setState({ message: "Problem: " + error.status });
              console.log("Problem: " + error.status)
           });
  }

  render() {
    return (
      <div style={divStyle}>
        Message: {this.state.message}
      </div>);
  }
}
akcoban
  • 953
  • 7
  • 14
  • Oh, I'm sensing that *state* is a reserved keyword (or at least a default variable) in React. Correct? – DonkeyBanana Sep 22 '18 at 23:11
  • @DonkeyBanana - A standard property of `React.Component` instances, yes; more: https://reactjs.org/docs/state-and-lifecycle.html – T.J. Crowder Sep 22 '18 at 23:19
  • Yes, similiar with props but states are private and controlled by component. You should check Redux and MobX after understand the states and lifecycles. These are libraries for easy controlling your states. – akcoban Sep 22 '18 at 23:20
  • @guvenakcbn Hmm... I'm hoping to resolve this particular part without adding Redux etc. (that's the "tomorrow" thing, hehe). I noticed that the code you suggested complains *property message does not exist on type 'ReadOnly<{}>'*, though. Am I missing something? – DonkeyBanana Sep 22 '18 at 23:24
  • @DonkeyBanana Is it running on .ts files? – akcoban Sep 22 '18 at 23:36
  • @guvenakcbn It's a *TSX* file. However, now that you've got me going a bit, I was able to google it myself too (still shaky-flaky, though). I suspect that I need to specify the state of my class in its definition, as suggested [here](https://stackoverflow.com/a/47562985/1525840). What do you think? Am I totally off or barking the right tree? – DonkeyBanana Sep 22 '18 at 23:38
  • @DonkeyBanana I didn't use React with TypeScript. So i am not sure about that, i don't want give you wrong answer. If you use that with .js files, should work perfectly. You can try the solution in your ref link. – akcoban Sep 22 '18 at 23:43
  • @guvenakcbn Got it. Your reply did got my butt going further so I'm accepting it as the answer. Now, I have keywords and much better idea what to google. I only need to find out how to render an array and not only a scalar and then, off to bed. Thanks, mate! – DonkeyBanana Sep 22 '18 at 23:53