简体   繁体   中英

How to use recompose's toClass HOC to add a ref to a functional component?

I'm trying to add a ref to a functional component in React Native as to use scrollToEnd on the FlatList component.

I would like to use recompose for this and as their docs state, toClass() should be able to handle this. However, no examples are given.

Currently, this is my failing implementation. Could someone tell me what I'm doing wrong?

I'd be much obliged!

import React from 'react';
import PropTypes from 'prop-types';
import { FlatList, View, Text } from 'react-native';
import { graphql } from 'react-apollo';
import { compose, toClass, lifecycle } from 'recompose';

import CommentItem from './CommentItem';
import { commentsQuery } from '../../queries/comments';

const CommentScreen = ({ onRef, currentUser, data: { comments, loading } }) => {
  if (loading) {
    return (
      <View>
        <Text>Loading...</Text>
      </View>
    );
  }

  return (
    <FlatList
      ref={ref => {
        this.refs.commentList = ref;
      }}
      data={comments}
      keyExtractor={item => item.id}
      renderItem={({ item }) => <CommentItem {...item} currentUser={currentUser} />}
    />
  );
};

export default compose(
  toClass,
  graphql(commentsQuery),
  lifecycle({
    componentDidMount() {
      console.log('PROPZZZ', this.commentList);
    },
  }),
)(CommentScreen);

CommentScreen.propTypes = {
  fetchComments: PropTypes.func.isRequired,
  updateId: PropTypes.number.isRequired,
  comments: PropTypes.arrayOf(Object),
  text: PropTypes.string.isRequired,
};

Just use a class components instead of a functional component if you need to use ref . However, if for some reasons you need to use a functional component, you can use withHandlers . Please see the answer of this SO question for how to use it.

You have to access your element from this.refs inside the lifecycle method.

lifecycle({
  componentDidMount() {
    console.log('PROPZZZ', this.refs.commentList);
    //                          ^^^^
  }
})

You can see it working here : https://snack.expo.io/Sy-rCBQhW

I'm going to paste some code I managed to make work with a similar problem, I know it is not exactly your code but it shows the solution:

import React from 'react'
import { compose, withState, withHandlers, lifecycle } from 'recompose'

import Left from './Left'
import Right from './Right'
import Modals from './Modals'
import { ActionBar, Container } from './styles'

let ref = null

const enhance = compose(
  withState('position', 'setPos', 'relative'),
  withHandlers({
    isTop: ({ setPos }) => () => {
      const { top } = ref && ref.getBoundingClientRect()
      top && setPos(top <= 0 ? 'fixed' : 'relative')
    },
  }),
  lifecycle({
    componentDidMount() {
      window.addEventListener('scroll', this.props.isTop)
    },
    componentWillUnmount() {
      window.removeEventListener('scroll', this.props.isTop)
    },
  })
)

export default enhance(({ position, ...props }) => (
  <Container innerRef={el => (ref = el)}>
    <ActionBar position={position}>
      <Left {...props} />
      <Right {...props} />
      <Modals {...props} />
    </ActionBar>
  </Container>
))

As you can see the key here was storing the ref in a variable and use a mix of withState + withHandlers + lifecycle to make it work, I didn't need to use the toClass HoC in the end because as long as you use any other HoC from recompose you are already using React component classes underneath :)

Hope it helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM