Search code examples
reactjsreact-nativereact-native-gesture-handler

Trigger method on parent component from child component - react


I have a swipeable component with an open and close function that I would like to trigger from a child of the component. I don't think using state to do it in this instance would be correct since I want to avoid a re-render while the swipeable component is animating (please correct me if I'm wrong).

I'm using react-native-gesture-handler/swipeable and following their example here

SwipeCard component

import React, { useRef } from 'react';

import { RectButton } from 'react-native-gesture-handler';
import Swipeable from 'react-native-gesture-handler/Swipeable';
import Animated from 'react-native-reanimated';
const AnimatedView = Animated.createAnimatedComponent(View);

export const SwipeCard = ({ children }) => {
  let swipeableRow = useRef(null);

  let renderRightActions = (_progress, dragX) => {
    let scale = dragX.interpolate({
      inputRange: [-80, 0],
      outputRange: [1, 0],
      extrapolate: 'clamp',
    });
    return (
      <RectButton style={styles.rightAction} onPress={close}>
        {/* Change it to some icons */}
        <AnimatedView style={[styles.actionIcon]} />
      </RectButton>
    );
  };

  let close = () => {
    swipeableRow?.close();
  };

  let open = () => {
    swipeableRow?.openRight();
  };

  return (
    <Swipeable
      ref={swipeableRow}
      renderRightActions={renderRightActions}
      friction={2}
      rightThreshold={40}
    >
      {children}
    </Swipeable>
  );
};

Below is the component where I'm using SwipeCard and the Toggle is the event I want to use to fire the open() method in the SwipeCard component.

<Row>
    {arr.map((item) => {
      return (
        <SwipeCard key={item.id}>
          <CardContainer>
            <CleaningCard
              cleaningData={item}
            />
            <Toggle onPress={() => {}}>
              <Icon name="dots" />
            </Toggle>
          </CardContainer>
        </SwipeCard>
      );
    })}
  </Row>

Solution

  • You can use the render prop pattern and pass close, open as arguments.

    Parent component where SwipeCard is used:

    <Row>
      {arr.map((item) => {
        return (
          <SwipeCard key={item.id}>
            {({ close, open }) => (
              <CardContainer>
                <CleaningCard cleaningData={item} />
                <Toggle
                  onPress={() => {
                    // close()/open()
                  }}
                >
                  <Icon name="dots" />
                </Toggle>
              </CardContainer>
            )}
          </SwipeCard>
        );
      })}
    </Row>;
    

    SwipeCard component:

    <Swipeable
      ref={swipeableRow}
      renderRightActions={renderRightActions}
      friction={2}
      rightThreshold={40}
    >
      {children({close, open})}
    </Swipeable>
    

    We're simply making the children a function that takes an object and returns the JSX. The required object is passed as an argument (children({close, open})).