modernator
modernator

Reputation: 4427

In componentDidUpdate refs is undefined

I want to use Chart.js on my website. As you can see title, I'm using React.js. To use Chart.js, I need the canvas and context like this:

let context = document.getElementById('canvas').getContext('2d');
let chart = new Chart(context, ...);

so I design the component like this:

export function updateChart() {
    let context = this.refs.chart.getContext('2d');
    let chart = new Chart(context ,... );
    ...
}

export default class GraphChart extends React.Component {
    constructor() {
        super();
        updateChart = updateChart.bind(this);
    }
    componentDidMount() {
        updateChart();
    }
    render() {
        return <canvas ref="chart" className="chart"></canvas>;
    }
}

as you can see, I exported two things, update chart function and GraphChart class. Both will using in parent component like this:

import { updateChart } from './GraphChart';
import GraphChart from './GraphChart';

class Graph extends React.Component {
    ...
    someKindOfAction() {
        // update chart from here!
        updateChart();
    }
    render() {
        return (
            <div>
                <SomeOtherComponents />
                <GraphChart />
            </div>
        );
    }
}

then Parent class using exported updateChart function to update chart directly. It was working, but only first time. After unmount and mount the GraphChart component, it's refs are just empty.

Why refs is empty? And If I did wrong way, how can I get canvas context for initialize Chart.js?

Upvotes: 3

Views: 2324

Answers (1)

jokka
jokka

Reputation: 1912

Object refs is undefined, because this is not what you think it is. Try logging it.

The function you’re exporting is not bound to this of your component. Or perhaps it is, but to the last created instance of your component. You can never be sure that’s the mounted instance. And even if you are, you can not use multiple instances at the same time. So, I would dismiss this approach entirely.

Other than that, providing the function to alter some component’s state is exactly the opposite of what’s React is trying to accomplish. The very basic idea is that the component should know to render itself given some properties.

The problem you are trying to solve lies in the nature of Canvas API, which is procedural. Your goal is to bridge the gap between declarative (React) and procedural (Canvas) code.

There are some libraries which do exactly that. Have you tried react-chartjs? https://github.com/reactjs/react-chartjs

Anyways, if you’re wondering how the hell should you implement it the “React way”, the key is to declare properties your component handles (not necessarily, but preferably), and then to use component lifecycle methods (e.g. componentWillReceiveProps and others) to detect when properties change and act accordingly (perform changes to the canvas).

Hope this helps! Good luck!

Upvotes: 1

Related Questions