Search code examples
reactjsrecompose

How do I rewrite this with 'recompose'?


I have this class component and want to rewrite it to stateless functional component with recompose:

export default class Popular extends Component {
    state = {
        value: 0,
        selected: "All",
        repos: null
    }

componentDidMount() {
    this.handleSelected(this.state.selected)
}

handleChange = (e, value) => {
    this.setState({ value })
}

handleSelected = lang => {
    this.setState({
        selected: lang,
        repos: null
    })
    fetchPopularRepos(lang).then(repos => {
        this.setState({
            selected: lang,
            repos
        })
    })
}

I'm struggling to combine onSelectLanguage and onFetchRepos in one function as in my code before refactoring. I don't know how to rewrite this for my componentDidMount function as well.

UPDATE:

got this working with:

const enhance = compose(
  withStateHandlers(initialState, {
    onChangeLanguage,
    onSelectLanguage
  }),
  lifecycle({
    componentDidMount() {
      fetchPopularRepos(this.props.selected).then(repos => {
        this.setState({
          repos
        })
      })
    }
  }),
  lifecycle({
    componentDidUpdate(prevProps) {
      if (this.props.selected !== prevProps.selected) {
        this.setState({ repos: null })
        fetchPopularRepos(this.props.selected).then(repos => {
          this.setState({
            repos
          })
        })
      }
    }
  })
)

These lifecycles don't look very sexy though. Not sure if this worth refactoring.


Solution

  • Extending on the previous answer, you could use functional composition to combine the onFetchRepos and onSelectLanguage as required. If I understand your requirements correctly, you should be able to achieve this by the following:

    const initialState = {
        value: 0,
        selected: "All",
        repos: null
    }
    
    const onChangeLanguage = props => (event, value) => ({
        value
    })
    
    const onSelectLanguage = props => lang => ({
        selected: lang
    })
    
    const onFetchRepos = props => (fetchPopularRepos(props.selected).then(repos => ({
        repos
    })))
    
    // Combined function: onFetchRepos followed by call to onSelectLanguage
    const onFetchReposWithSelectLanguage = props => onFetchRepos(props)
          .then(response => props.onSelectLanguage(response))
    
    // Minimal code to compose a functional component with both state handers and 
    // lifecycle handlers
    const enhance = compose(
        withStateHandlers(initialState, {
            onChangeLanguage,
            onSelectLanguage
        }),
        lifecycle({
            componentDidMount() {
                // Fetch repos and select lang on mount
                onFetchReposWithSelectLanguage(this.props)
            }
        })
    )
    

    Update

    // Minimal code to compose a functional component with both state handers and 
    // lifecycle handlers
    const enhance = compose(
        withStateHandlers(initialState, {
            onChangeLanguage,
            onSelectLanguage,
            setRepos
        }),
        lifecycle({
            componentDidMount() {
                // Reuse onSelectLanguage logic and call setState manually, use setState callback that
                // fires after state is updated to trigger fetch based on newState.selected language
                this.setState(onSelectLanguage(this.props.selected)(this.props), newState => {
                    fetchPopularRepos(newState.selected).then(repos => {
                        this.setState({
                            repos
                        })
                    })
                })
            }
        })
    )
    

    Hope this helps you!