GibboK
GibboK

Reputation: 73908

How to avoid using setState with callback

I have the following react component (it works), but I am using a cb for setState.

I would like to know how to refactory the code removing the cb from the code: this.setState({ viewer: null, document: null }, () => this.getViewer(type, item))

export class DocumentComponent extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      document: null,
      viewer: null,
    }
  }

  getViewer(type, item) {
    let node = null
    switch (type) {
      case 'image':
        node = (
          <Imager url={item.src} />
        )
        this.setState({ viewer: node, document: item })
        break
      default:
        this.setState({ viewer: null, document: null })
        return null
    }
  }

  openViewer(type, item) {
    this.setState({ viewer: null, document: null }, () => this.getViewer(type, item))
  }

  handlerOnClick(item: Object) {
    this.openViewer('image', item)
  }

  render() {
    const { tileData, classes } = this.props
    return (
      <div className={classes.root}>
        <Thumbnailss tileData={tileData} handlerOnClick={item => this.handlerOnClick(item)} />
        {this.state.viewer}
      </div>
    )
  }
}

export default DocumentComponent

Upvotes: 1

Views: 553

Answers (3)

Abdennour TOUMI
Abdennour TOUMI

Reputation: 93173

I assume/ you assume you are using babel and ES7. If so, you can use async instead.

using cb

openViewer(type, item) {
   this.setState({ viewer: null, document: null }, () => this.getViewer(type, item)) 
}

using async-await

async openViewer(type, item) {
   await this.setState({ viewer: null, document: null });
   this.getViewer(type, item)
}

We were tested this approach and it works fine in our environments.

using promises

Or if you are comfortable with promises .

export class DocumentComponent extends React.Component {
   // override and extend setState 

   setState(state) {

     return new Promise((resolve) => {
        super.setState(state, resolve);
     });
   }
   //...
   //...

   openViewer(type, item) {
     this.setState({ viewer: null, document: null })
       .then(() => this.getViewer(type, item))
   }
}

Upvotes: 3

Mayank Shukla
Mayank Shukla

Reputation: 104369

Why not this way?

Instead of storing UI elements in state, store the data that you want to use, like src and type in your case. Call getViewer method from render it will return the proper ui items. By this way you don't need to worry about setState callback, whenever you update the value of type and src, react will update the ui automatically.

export class DocumentComponent extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      src: '',
      type: '',
    }
  }

  getViewer() {
    switch (this.state.type) {
      case 'image': return <Imager url={this.state.src} />
      default: return null
    }
  }

  handlerOnClick(item: Object) {
    this.setState({
      type: 'image',
      src: item.src
    })
  }

  render() {
    const { tileData, classes } = this.props
    return (
      <div className={classes.root}>
        <Thumbnailss tileData={tileData} handlerOnClick={item => this.handlerOnClick(item)} />
        {this.getViewer()}
      </div>
    )
  }
}

export default DocumentComponent

Upvotes: 3

ruanmer
ruanmer

Reputation: 31

Try this:

setViewer(type, item) {
  switch (type) {
    case 'image':
      const node = (
        <Imager url={item.src} />
      );
      this.setState({ viewer: node, document: item });
      break;
    default:
      this.setState({ viewer: null, document: null });
  }
}

openViewer(type, item) {
  this.setViewer(type, item);
}

Upvotes: 0

Related Questions