0

I am using Ag Grid in react+redux application. I need to make column state of the grid persistence. In other words everytime when user make changes with columns(order, sorting, hide\show, etc), application must save column state to DB. Then when user comes again, column state must be restored. My approach is to use columnApi.getColumnState() to push state to DB on various event callbacks and columnApi.setColumnState() in redux action from componentDidMount. I got no errors, however state is not applied. Here is my compoment sample code (I'm using react-thunk):

@(connect(selector, { getColumnStateAction, saveColumnStateAction }) as any)
class MyComponent extends Component<Props, {}> {

  private columnApi:any

  onGridReady(params) {
    const { columnState } = this.props
    params.columnApi.setColumnState(columnState)
    this.columnApi = params.columnApi
  } 

  onColumnChanged() {
    saveColumnStateAction(this.columnApi.getColumnState())
  }

  componentDidMount() {
    getColumnStateAction()
  }

  render() {        
    const { columnDefs } this.props
    const gridOptions = {
      columnDefs,
      onGridReady: this.onGridReady,
      onColumnVisible: this.onColumnChanged,
      onColumnResized: this.onColumnChanged,
      onColumnMoved: this.onColumnChanged,
      onColumnPinned: this.onColumnChanged
    }

    <AgGridReact { ...gridOptions } />
  }
}

And here is my actions:

const getColumnStateAction = () => async(dispatch:Dispatch) => {
   const columnState = await fetchFromDb();
   dispatch({ type:'GET_COLUMN_STATE', payload:columnState })
}

const saveColumnStateAction = (columnState:any) => async(dispatch:Dispatch) => {
   await saveToDb(columnState);
   dispatch({ type:'SAVE_COLUMN_STATE' })
}

My question is: where and when should I restore columnState so it will be applied to my grid. The point is that I had working code before going redux. That code below worked and the state was applied correctly:

@(connect(selector, { getColumnStateAction, saveColumnStateAction }) as any)
class MyComponent extends Component<Props, {}> {

  private columnApi:any

  onGridReady(params) {
    this.columnApi = params.columnApi
  } 

  async onColumnChanged() {
    await saveToDb(this.columnApi.getColumnState());
  }

  async componentDidMount() {    
    const columnState = await fetchFromDb();
    this.columnApi.setColumnState(columnState)
  }

  render() {
    const { columnDefs } this.props
    const gridOptions = {
      columnDefs,
      onGridReady: this.onGridReady,
      onColumnVisible: this.onColumnChanged,
      onColumnResized: this.onColumnChanged,
      onColumnMoved: this.onColumnChanged,
      onColumnPinned: this.onColumnChanged
    }

    <AgGridReact { ...gridOptions } />
  }
}

So I suspect that I am doing something wrong with redux. Please help.

KozhevnikovDmitry
  • 1,465
  • 10
  • 24

1 Answers1

0

I think, I have found the solution. It is probably not most elegant, but it works. The point is that I must call columnApi.setColumnState() only once at specific moment of time - after componentDidMount, onGridReady and when columns have been already appeared in the grid. So this is my working example:

@(connect(selector, { getColumnStateAction, saveColumnStateAction }) as any)
  class MyComponent extends Component<Props, {}> {

  public state:any = { columnStateSet: false }

  private columnApi:any

  onGridReady(params) {
    const { columnState } = this.props
    this.columnApi = params.columnApi
  } 

  onColumnChanged() {
    saveColumnStateAction(this.columnApi.getColumnState())
  }

  /**
  * Catches a moment for applying persisted column state
  * The point is that previous columnState, that we get from DB, must be applied in specific moment.
  * It must be applied once per loading component after all columns are already set and columnApi becomes available.
  * Otherwise column state will not be applied correctly.
  */
  private onGridColumnsChanged = () => {
    const { columnStateSet } = this.state
    const { columnState } = this.props

    if(this.columnApi && columnState && !columnStateSet) {
      this.columnApi.setColumnState(columnState)
      this.setState({ columnStateSet: true})
    }
  }

  componentDidMount() {
    getColumnStateAction()
    this.setState({ columnStateSet: false })
  }

  render() {
    const { columnDefs } this.props
    const gridOptions = {
      columnDefs,
      onGridReady: this.onGridReady,
      onColumnVisible: this.onColumnChanged,
      onColumnResized: this.onColumnChanged,
      onColumnMoved: this.onColumnChanged,
      onColumnPinned: this.onColumnChanged,
      onGridColumnsChanged: this.onGridColumnsChanged
    }

    <AgGridReact { ...gridOptions } />
  }
}

Hope it helps somebody.

KozhevnikovDmitry
  • 1,465
  • 10
  • 24