Search code examples
reactjsthisstatic-methodssetstate

Reactjs → static methods without access to "this", what's the alternative?


EDITED: Static methods don't have access to "this". The underlying question is then, how in reactjs should you organize the code, if you'd like to separate functionalities in different classes? the only way to call the methods of these classes is then by making them "static". Is it really the only way? What are you supposed to do? Create one big class so that all methods will have access to "this"?

EDITED2: What I have done is then to avoid writing a static method that needs to have access to the state. In particular, I've used a promise to return the value to the class that does have access to the state.

 static Parse(cvs_string) {
    return new Promise((resolve, reject) => {
        Papa.parse(cvs_string, {
            header: true,
            skipEmptyLines: true,
            complete: (results) => resolve(results)
        });
    });
 }

EDITED3: But, as said in the comments, it's nonesense to build a class that also extends from Component if the main reason is to provide helper functions, so at the end:

import Papa from 'papaparse';

export const ParseCsv = (csv_string) => {
    return new Promise((resolve, reject) => {
        Papa.parse(csv_string, {
            header: true,
            skipEmptyLines: true,
            complete: (results) => resolve(results)
        });
    });
}

---- [previous]

Why is this not working? Shouldn't I have access to setstate here?

import React, { Component } from 'react';
import Papa from 'papaparse';

class PapaParse extends Component {

  constructor(props) {
    super(props);
    this.Parse = this.Parse.bind(this);
    this.updateData = this.updateData.bind(this);
  }

  static Parse(cvs_string) {
    Papa.parse(cvs_string, {
      header: true,
      skipEmptyLines: true,
//    complete: (results) => { // this gives the same error
      complete: function(results) {
        PapaParse.updateData(results);
      }
    });
  }

  static updateData(results) {
    console.log(results); // results are the expected ones
    PapaParse.setState({data: results.data}); // here the error, or
    this.setState({data: results.data}); // here the same error
  }

}

export default PapaParse;

I can solve this by sending "this" as a variable, in

PapaParse.Parse(response, this);

and then in the PapaParse component

static Parse(cvs_string, that) {
...
PapaParse.updateData(results, that);
...
static updateData(results, that) {
...
that.setState({data: results.data});

So I understand that the "this" is lost when I'm calling a method of a componenet without invoking it with the "tag", and merely calling it as a static method.

Then, what I'm doing here is what I'm supposed to do? Or what would be the best way to do this?


Solution

  • Static methods are intended for code that doesn't depend on class instance, because there's none. There are not so many good use cases for static methods, because if a function isn't directly linked to a class as an entity, it possibly doesn't need to be a part of it. One of use cases is React component getDerivedStateFromProps hook which is pure function that needs to defined as a method (because it's a hook that should be accessed as class property by the framework), it forces a developer to not use class instance and focus on function input and output.

    Since the method needs class instance and setState instance method in particular, static methods are not applicable here. None of these methods should be static:

    class PapaParse extends Component {
      Parse(cvs_string) {
        Papa.parse(cvs_string, {
          header: true,
          skipEmptyLines: true,
          complete: (results) => {
            this.updateData(results);
          }
        });
      }
    
      updateData(results) {
        console.log(results);
        this.setState({data: results.data});
      }
    
    }
    

    This is same problem as explained in this answer:

    this.Parse = this.Parse.bind(this);
    

    It's a mistake to bind static method to class instance, especially in a class that isn't a singleton by design and is expected to be instantiated multiple times (React component class is). There can be multiple class instances, this may result in bugs and memory leaks.

    If Parse method is supposed to be triggered outside this class, this should be done in a way that is idiomatic to React, e.g. get PapaParse component ref in parent component and access instance method on it:

    // in constructor
    this.papaParseRef = React.createRef();
    ...
    // in render
    <PapaParse ref={this.papaParseRef}/>
    

    The method will be available as this.papaParseRef.current.Parse() after render.