简体   繁体   中英

Why is the state value not reflecting in ReactJS?

class App extends Component {
  state = {
    name: ""
  };
  abc() {
    console.log("ddd");
    this.setState({
      name: "asd"
    });
  }
  render() {
    return <div>ddd{this.state.name}</div>;
  }
}

this.state.name is not reflecting. https://codesandbox.io/s/vigorous-sky-woxr7

I am calling this function from outside.

I want to call the component function from outside and update the state.

That's not the way you write React code. You should do something like this:

import React, { Component } from 'react';

class App extends Component {

  constructor(props) {
    super(props);
    this.state = {
      name: ""
    }
  }

  setName = _ => {
    this.setState({ name: 'John Doe' })
  }

  render() {
    return (
      <>
        <div>
          {`This is my name: ${this.state.name}`}
        </div>
        <button onClick={this.setName}>Click me to see my name!</button>
      </>
    );
  }
}

export default App;

Two important keys to take into account:

  1. The state must be initiated within the constructor
  2. You must have a specific function to update your state that you can call from wherever you want. If you want to initiate it at the beginning of the lifecycle use componentDidWount() method.

UPDATE : If you want to call it from outside that component

In this case you'll have two different components and if you want to call a method from the child one you should do the following:

Parent Component

import React, { Component } from 'react';
import MyComponent from './MyComponent'

class App extends Component {
   onClick = () => {
    this.myComponent.method()
  }
  render() {
    return (
      <div>
        <MyComponent onRef={ref => (this.myComponent = ref)} />
        <button onClick={this.onClick}>MyComponent.method()</button>
      </div>
    );
  }
}

export default App;

Child Component

import React, { Component } from 'react';

class MyComponent extends Component {
  componentDidMount() {
    this.props.onRef(this)
  }
  componentWillUnmount() {
    this.props.onRef(undefined)
  }
  method() {
    window.alert('Hello there, my name is John Doe!')
  }
  render() {
    return <h1>Example of how to call a method from another component</h1>
  }
}

export default MyComponent;

Take a look to a live example here: https://repl.it/repls/AfraidNecessaryMedia

I recommend you to call your class method inside the class, but if you want a solution to your problem use React.createRef() on your index.js:

const rootElement = document.getElementById("root");
const ref = React.createRef();
ReactDOM.render(<App ref={ref} />, rootElement);
ref.current.abc();

https://codesandbox.io/embed/sleepy-glitter-fwplz

import React, { Component } from "react";
import ReactDOM from "react-dom";

import "./styles.css";
window.a = function() {
  ref.current.abc();
};

class App extends Component {
  state = {
    name: ""
  };
  abc() {
    console.log("ddd");
    this.setState({
      name: "asd"
    });
  }
  render() {
    return <div>ddd{this.state.name}</div>;
  }
}

const rootElement = document.getElementById("root");
const ref = React.createRef();
ReactDOM.render(<App ref={ref} />, rootElement);
window.a();

How about this?

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: ""
    };
  }

  componentDidMount() {
    this.abc();
  }

  abc() {
    console.log("ddd");
    this.setState({
      name: "asd"
    });
  }

  render() {
    return <div>ddd {this.state.name}</div>;
  }
}

The way you called the function was not correct.
Remember: a function needs to be called...
For your second question: you can't call a function from the child into the parent.
In that case you should create a function in the parent, send to the child , then call it on a event (component load, click, something..).
If you really need to do that: send data, functions and more from the child to the parent... check Redux : https://redux.js.org/

Here is your code working (little change on function abc):

import React, { Component, Fragment } from "react"
import ReactDOM from "react-dom"    
import "./styles.css"

window.a = function() {
  var ab = new App()
  ab.abc()
}

class App extends Component {

state = {
    name: ""
  }

  abc = () => {
    this.setState({ name: "new name" })
  }

  render() {
    return (
      <Fragment>
        <div>{this.state.name}</div>
        <button onClick={this.abc}>call function</button>
      </Fragment>
    );
  }
}

const rootElement = document.getElementById("root")
ReactDOM.render(<App />, rootElement)
window.a()

Please note that using ';' in the end of each line, is not the correct aproach to write React js.
Best practices link: https://americanexpress.io/clean-code-dirty-code/

You need to do two things...

  1. Bind the abc() to this in constructor or use arrow functions like how I did, else you won't get access to this.setState() .
  2. Call the function in the lifecycle method, not outside React using ComponentDidMount() .

Here's the updated code:

import React, { Component } from "react";
import ReactDOM from "react-dom";

class App extends Component {
  state = {
    name: ""
  };
  abc = () => {
    console.log("ddd");
    this.setState({
      name: "asd"
    });
  };
  componentDidMount() {
    this.abc();
  }
  render() {
    return <div>ddd {this.state.name}</div>;
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Demo in CodeSandbox

because abc() not bounded to this and method use setState which is a method in this object, you have two choices two solve this problem:

1- bind this to method in constructor

2- implement your function as an arrow function instead of default function

for first approach:

class App extends Component {
  constructor(props){
      super(props);
      this.state = {
          name: '',
      }

      this.abc = this.abc.bind(this);
  }
  abc(){
    console.log("ddd");
    this.setState({
      name: "asd"
    });
  };
  componentDidMount() {
    this.abc();
  }
  render() {
    return <div>ddd {this.state.name}</div>;
  }
}


for the second approach:

class App extends Component {
  state = {
      name: '',
  }
  abc = () => {
    console.log("ddd");
    this.setState({
      name: "asd"
    });
  };
  componentDidMount() {
    this.abc();
  }
  render() {
    return <div>ddd {this.state.name}</div>;
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM