3

I have the following ReactJS component:

articles_list.jsx

import React from 'react';
import marked from 'marked';
import './articles_list.css';

export default class ArticlesList extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      articles: null
    }
  }

  componentWillMount() {
    fetch('/articles_all')
    .then(res => res.json())
    .then(json => {
      this.setState({
        articles: json.articles
      });
    });
  }

  handleClick(e) {
    e.preventDefault();
    var elem = e.target;
    var file = elem.getAttribute('data-file').split('.')[0];
    fetch('/article/'+file, {
      headers: {
        'Accept': 'text/markdown'
      }
    })
    .then(res => res.text())
    .then(txt => marked(txt))
    .then(html => document.getElementById('article-text').innerHTML = html)
  }

  render() {

    var teste = []

    if (this.state.articles === null) {
      teste.push(<div id="no-articles" key="1">No articles</div>)
    } else {
      {this.state.articles.forEach( function(element, index) {
        teste.push(<div onClick={this.handleClick} data-file={element.file} className="articles-menu-item" key={index.toString()}>{element.title}</div>);
      }.bind(this))}
    }

    return(
      <div className="articles-list">
        <div className="articles-list-title">
          ARTICLES
        </div>
        <div id="menu-body" className="menu-body">{teste}</div>
      </div>
    );
  }
}

As you may see, it fetches as list of articles and creates links. When these links are clicked the corresponding article is load in a certain area of the page.

The code is working perfectly, but now I need to load a certain article before any of the links to be clicked. Then I decided to break the code inside handleClick like this:

   loadArticle(file) {
     fetch('/article/'+file, {
       headers: {
         'Accept': 'text/markdown'
       }
     })
     .then(res => res.text())
     .then(txt => marked(txt))
     .then(html => document.getElementById('article-text').innerHTML = html)
   }

   handleClick(e) {
    e.preventDefault();
    var elem = e.target;
    var file = elem.getAttribute('data-file').split('.')[0];
    loadArticle(file);
  }

My idea with this is to invoke loadArticle inside render to load an specific article when the component loads, like this:

return(
  <div className="articles-list">
    <div className="articles-list-title">
      ARTICLES
    </div>
    <div id="menu-body" className="menu-body">{teste}{this.loadArticle('my_specific_article')}</div>
  </div>
);

It works and now my_specific_article loads correctly when I navigate to the page. But...

But now when I click the links, that were working fine before, I got an error

Uncaught ReferenceError: loadArticle is not defined

And if I do

   handleClick(e) {
    e.preventDefault();
    var elem = e.target;
    var file = elem.getAttribute('data-file').split('.')[0];
    this.loadArticle(file);
  }

using this, then I get

Uncaught TypeError: Cannot read property 'loadArticle' of null

How should I deal with this? I know it is a matter of context but, being new to ReactJS, I really don't know how to proceed.

EDIT

This is not a duplicate of this question, as it was marked. The reason is simple. In the mentioned question the function to bind is a event handler and in my problem a function being called by another. In fact, my event handler (similar to the other question) was working fine without the bind, then we are not talking about the same thing.

Ed de Almeida
  • 3,675
  • 4
  • 25
  • 57

1 Answers1

5

You must bind your custom functions in the constructor. Modify your constructor like this.

constructor(props) {
  super(props);
  this.state = {
    articles: null
  }
 this.handleClick = this.handleClick.bind(this);
 this.loadArticle = this.loadArticle.bind(this);
}

Now everywhere you should call this function as this.handleClick & this.loadMore.

You can read about other binding patterns here. The one I've mentioned is number 4.

That is the preferred way in facebook docs as well.

  • Okay, it works! But I still can't understand one thing. `handleClick` was working fine even before I do this. Is that because it was being invoked as an event handler? I'm still a bit confused about the different contexts in ReactJS. But thanks for the answer and for the documentation. I'll read it now to try to make these things clear in my mind. – Ed de Almeida Jun 07 '17 at 08:08
  • 1
    handleClick was working fin ebefore becasue, you were not using `this` inside the function. when you wanted to call `this.loadArticle` , here `this` refered to the handleClick context where loadArticle was not available it gave ann error – Shubham Khatri Jun 07 '17 at 08:13
  • What if I want to pass some parameters in function. like this.loadArticle("param"). This does'nt work..... – Mustkeem K Oct 26 '18 at 05:52