karolis2017
karolis2017

Reputation: 2415

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.

Upvotes: 1

Views: 1318

Answers (2)

Dacre Denny
Dacre Denny

Reputation: 30360

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!

Upvotes: 0

Dacre Denny
Dacre Denny

Reputation: 30360

This looks like a case where you'd want to use the lifecycle() method. I'm not a recompose expert, however I think the following adjustments might achieve what you're after:

const onFetchPopularRepos = props => () => {
    // Make sure the method returns the promise
    return fetchPopularRepos(props.selected).then(repos => ({  
        repos: repos
    }))
}

const withPopularReposState = withStateHandlers(initialState, {
    onChangeLanguage,
    onSelectLanguage
})

// Add a life cycle hook to fetch data on mount
const withLifecycle = lifecycle({
  componentDidMount() {

    // Update the state
    onFetchPopularRepos(this.props).then(newState => this.setState(newState)) 
  }
})();

// Compose the functional component with both lifecycle HOC
const enhance = withLifecycle(withPopularReposState)

Upvotes: 1

Related Questions