Issue reloading new tasks in Gantt even using destructor

Hi there,

I’ve been trying to implement this gantt component in my application for a while.
I am new to react, so I may not be implementing the best way, but I am trying to.

I am having a problem to reload my tasks after closing the component and opening it again.

I was able to make it work the way I want, but let me give you an idea of my scenario here:
In my application the user is able to create many projects, and each project should display its many actions on Gantt. That’s my goal with the component. So, basically, the user sees his projects in a list and he is able to load the tasks of any project in this list.

Whenever I close the component in order to load tasks of other project, I am getting an error related to the state variable. Therefore, I am not sure if I am using it the correct way.

I have been following the tutorial provided in the website, pretty easy to set up by the way.

I my list I call for “edit-gantt.jsx”, which will render the gantt component:

[code] render (){

let acoes = this.state.acoes && this.state.acoes.map((acao) => (
  {
         //setting state in componentDidMount. I go to my controller, and return a list of tasks
        // state will map my tasks in a list here 
  }
));

let data = {
  data:[].concat(acoes)
};
return(
  <Content titulo="Gantt">
    <Row>
      <Col md={12}>
        <LoadingContainer isLoading={this.state.isLoading}>
          <div style={{marginTop: 15, height: 700, padding:5}}>
            <div className="gantt-container">
              {
               acoes &&
                <Gantt
                  tasks={data}
                  zoom={this.state.currentZoom}
                  onTaskUpdated={this.handleTaskUpdate}
                  onLinkUpdated={this.logLinkUpdate}
                  configuracoesColunas={this.state.configuracoes}
                  newTasks={data}
                  handleCloseAcao={this.handleCloseAcao}
                  itemId={this.props.params.id}
                />
              }
            </div>
          </div>
        </LoadingContainer>
      </Col>
    </Row>
  </Content>
);

}[/code]

Now, here is my gantt component that will be rendered:

[code]
export default class Gantt extends React.Component {
constructor(props){
super(props);
}

shouldComponentUpdate(nextProps){
// if (this.props.tasks.data.length !== nextProps.tasks.data.length){
// return true
// }
// if (this.props.zoom !== nextProps.zoom){
// return true
// }
//else {return false}
gantt.clearAll();
gantt.parse(nextProps.tasks); // loading new tasks

gantt.refreshData();
return this.props.zoom !== nextProps.zoom;

}

componentDidUpdate() {
gantt.render();
}

//componentWillMount(){
// gantt.init(this.ganttContainer);
//}

componentWillUnmount(){
gantt.clearAll();
//gantt.destructor();
//gantt.detachEvent()
}

handleClose(){
this.props.handleCloseAcao();
}

componentDidMount() {
// configurando as colunas do gantt
gantt.config.columns = [
my columns
];

if(!gantt.$eventsAttached){
  gantt.$eventsAttached = true;

  gantt.attachEvent('onTaskDblClick', (id) => {
    if (id != null){
      let task = gantt.getTask(id);
      ContentManager.addContent(<AcaoEdit
        item={task}
        id={id}
        handleClose={this.handleClose}
        itemId={this.props.itemId}>
      </AcaoEdit>);
    }
    return false;
  });

  /*
  gantt.attachEvent("onDestroy", () => {
    console.log("destrói gantt");
  });*/

}

gantt.config.layout = {
  css: "gantt_container",
  rows:[
    {
      cols: [
        {view: "grid", id: "grid", scrollX:"scrollHor", scrollY:"scrollVer"},
        {resizer: true, width: 1},
        {view: "timeline", id: "timeline", scrollX:"scrollHor", scrollY:"scrollVer"},
        {view: "scrollbar", scroll: "y", id:"scrollVer"}
      ]
    },
    {view: "scrollbar", scroll: "x", id:"scrollHor", height:20}
  ]
};

gantt.init(this.ganttContainer);
gantt.parse(this.props.tasks);

}

render() {
this.setZoom(this.props.zoom);
return (
<div
ref={(input) => this.ganttContainer = input }
style={{width: ‘100%’, height: ‘400’}}
>
);
}
}[/code]

This is not the entire code, I removed a few things in order to make it easier to understand my scenario.

Here is my problem, with this code I was able to make it work.
I send my tasks to the component, it gets rendered, and then “componentDidMount” loads my tasks. To create new tasks I’ve created my own method. There is a button that opens a modal, the user inserts data, saves, and then I call a method “handleClose” that will update my state variable loading tasks from the back end again. This is working fine, until I close gantt, return to my list of projects and try to load tasks of another project.

I left some commented lines in the code so you know a few different things I’ve tried already.

There seems to be an issue with state, here is the error I am getting:
warning.js:33 Warning: setState(…): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the FormEdit component.

Last thing I tried was to use the destructor:

componentWillUnmount(){ gantt.clearAll(); gantt.destructor(); //gantt.detachEvent() }

When I close the component, and try to open again, it returns me:
dhtmlxgantt.js:10 Uncaught (in promise) TypeError: Cannot read property ‘tasksStore’ of undefined

Taking a look at the DOM, was removed, but the is still there empty.

Am I doing it wrong? Does anybody have a better idea of how to use the component?

Hi,
a gantt instance becomes unusable after the destructor method is called.
The main use of the destructor is cleaning new instances of gantt in the enterprise builds, which provide a factory method for creating new instances.

Which means that in an enterprise build, you can do the following:
you can create a new gantt instance on componentDidMount:

[code] componentDidMount() {
// create new gantt instance when the component is mounted
this.gantt = Gantt.getGanttInstance();

...

}[/code]
and destroy when the component is unmounted:

componentWillUnmount() { this.gantt.destructor(); this.gantt = null; }
That way you’ll be working with a fresh instance of gantt and won’t have to worry that the previous state of gantt could have some side effects on the current display.

In gpl or a commercial build a destructor is not very useful - you have a single instance of gantt and if you destroy it, gantt will be unavailable until user reloads the page.
In this case, it’s enough to simply clear gantt data before unmount:

componentWillUnmount(){ gantt.clearAll(); }
and to make sure that the certain settings, such as event listeners are applied once per gantt instance and not on every componentDidMount

[code] componentDidMount() {
// configure gantt

// settings that should be applied only once per page lifetime:
if(!gantt.$eventsAttached){
  gantt.$eventsAttached = true;
  gantt.attachEvent('onTaskDblClick', (id) => {...
}

// other settings
...
// add gantt to the DOM and load data
gantt.init(this.ganttContainer);
gantt.parse(this.props.tasks);

}[/code]

Which is what you did now, and it seems correct.

I’m not sure why the setState error happens. Can you attach a zipped demo or put it to a github repo so I could do some checking?

1 Like