1

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

3
  • Why do you even call this.setState({ viewer: null, document: null }, ...)? Why not just execute this.getViewer(type, item) directly instead? Commented Nov 1, 2017 at 19:08
  • I am using this.setState({ viewer: null, document: null } to remove the component rendered at {this.state.viewer} if you know a better approch please let me know Commented Nov 1, 2017 at 19:12
  • 2
    But this.getViewer is already doing that as well. It either creates a new component or sets the state to null. I don't see why a previous call to set everything to null is necessary. Commented Nov 1, 2017 at 19:18

3 Answers 3

3

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))
   }
}
Sign up to request clarification or add additional context in comments.

2 Comments

ES7 is ES2016, not ES2017 (which introduced async/await). (Before ES2016 was published, "ES7" referred to experimental features, but some of these features are not experimental anymore).
@FelixKling Thanks for the info , I Updated the answer with 3rd alternative using promises.
3

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

6 Comments

tried but I still get the same issue, the component is not being updated
can you show the full code that you are trying on pastebin or somewhere? because this should work. also debug the code by console, put a console.log('item', item) inside handlerOnClick function and check what value you get on onClick.
I really like your approach, I hope you can give me an hit on how to fix it, thanks
i have doubt on this part handlerOnClick={item => this.handlerOnClick(item)} i think this item should be event object, make sure item have the src, do console.log(item) and please check its value inside handlerOnClick method.
yes I double check it, the state is actually updated properly when getViewer run. this.setState is asynch, could be the reason why it does not work?
|
0

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);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.