computerjazz / react-native-draggable-flatlist

A drag-and-drop-enabled FlatList for React Native
MIT License
1.96k stars 413 forks source link

How to expose ref set by onRef (need to call scrollToIndex from another file) #224

Open eric-om opened 4 years ago

eric-om commented 4 years ago

I would like to expose the flatList ref so that I can call scrollToIndex from another component in my screen. Currently my draggable flat list lives in its own component file and I am able to use the ref within that file with the following code:

onRef={ref => {try {listRef = ref} catch {console.log("Failed to set ref.")}}}

(I am also unsure how listRef gets set because we do not declare it anywhere.)

I'm unable to set the ref to a predeclared variable or to the component's state using the useState hook.

computerjazz commented 4 years ago

this may help: https://github.com/computerjazz/react-native-draggable-flatlist/issues/186#issuecomment-639654613

note that onRef returns the react ref, i.e. { current: flatLisRef | null } and ref.current will be null on first render.

eric-om commented 4 years ago

Thanks for the response!

So I'm trying to do something like this:

ChildComponent.js:
export default const ChildComponent = ({ myRef }) => {
  return (
  <DraggableFlatList
    onRef={ref => {try {myRef = ref } catch {console.log("Failed to set ref.")}}}
  }; />
)};

ParentComponent.js:
import ChildComponent from './ChildComponent';

const ParentComponent = () => {
  let myRef;

  return (
    <View>
      <Button onPress={ myRef.current._component.scrollToIndex({ animated: true, index: someIndex }); } />
      <ChildComponent myRef={myRef} />
    </View>
  )
}

I have gotten this to work using your example:

ChildComponent.js:
export default const ChildComponent = () => {
  return (
    <View>
      <Button onPress={ myRef.current._component.scrollToIndex({ animated: true, index: someIndex }); } />
      <DraggableFlatList
        onRef={ref => {try {myRef = ref } catch {console.log("Failed to set ref.")}}}
      }; />
    </View
  )
};

But I want to be able to call scrollToIndex from ParentComponent. When I try to call it from the ParentComponent it tells me myRef is undefined.

nicoglennon commented 4 years ago

@eric-om were you able to figure out how to do this? I am having the same issue -- having the ref live outside the childComponent with the DraggableFlatList causes it to get set to undefined.

creambyemute commented 4 years ago

@nicoglennon @eric-om

I'm using it pretty much like this, though Mobx is observable so it might be different with Redux/Context.

class MyComponentList extend React.PureComponent<...> {
    draggableListRef: React.RefObject<AnimatedFlatListType<InspectionGroupItem>>;

    onDraggableFlatlistRef = (ref: React.RefObject<AnimatedFlatListType<InspectionGroupItem>>) => {
        this.draggableListRef = ref;
        // Mobx State management
        this.props.rootStore.uiStore.setInspectionDraggableFlatlistRef(ref);
       // Redux do something else, context as well
      ...
    };

    <DraggableFlatlist
       onRef={this.onDraggableFlatlistRef}
       ...
    />
}

In another component:
class MyParentComponent extend React.Component<...> {
    onSwitchEditMode = () => {
        this.props.rootStore.uiStore.draggableInspectionFlatlistRef.current._component.scrollToIndex({
            index: this.props.activeIndex
        });
     }
}
eric-om commented 4 years ago

@nicoglennon @eric-om

I'm using it pretty much like this, though Mobx is observable so it might be different with Redux/Context.

class MyComponentList extend React.PureComponent<...> {
    draggableListRef: React.RefObject<AnimatedFlatListType<InspectionGroupItem>>;

    onDraggableFlatlistRef = (ref: React.RefObject<AnimatedFlatListType<InspectionGroupItem>>) => {
        this.draggableListRef = ref;
        // Mobx State management
        this.props.rootStore.uiStore.setInspectionDraggableFlatlistRef(ref);
       // Redux do something else, context as well
      ...
    };

    <DraggableFlatlist
       onRef={this.onDraggableFlatlistRef}
       ...
    />
}

In another component:
class MyParentComponent extend React.Component<...> {
    onSwitchEditMode = () => {
        this.props.rootStore.uiStore.draggableInspectionFlatlistRef.current._component.scrollToIndex({
            index: this.props.activeIndex
        });
     }
}

Good to hear that you got it working for you! I'll try storing it through context and report back. (Will test before end of year for those waiting for on a solution.)

nicoglennon commented 4 years ago

thanks @eric-om, let me know if you need more details.

eric-om commented 3 years ago

@nicoglennon @creambyemute I wasn't able to get it to work using hooks/context. I also tried experimenting with rxjs but I couldn't figure out how to observe ref.

Something that I think could work is writing functions in your child component to interact with the flatlist and then exposing those functions through a ref. I wanted to try this but my child component is very complex and this feature isn't worth the re-write at the moment.

nicoglennon commented 3 years ago

@eric-om will give that a try, thanks!

ChrisRArendt commented 3 years ago

Hello, I am trying to figure this out. I have a functional component but I don't think that should cause a problem.. however..

I tried

const [flatListRef, setFlatListRef] = React.useState(null);
onRef={ref=>{ setFlatListRef(ref); }}

Which said "Warning: Cannot update a component from inside the function body of a different component."

I tried

let flatListRef;
onRef={ref=>{ flatListRef = ref; }}

Which resulted in flatListRef always being undefined (scope is stale).

I tried

const flatListRef =React.useRef(null);
flatListRef = ref;

But flatListRef is a constant so now I can't set it...

I tried

const flatListRef =React.useRef(null);
onRef={flatListRef}

But it says "TypeError: onRef is not a function. (In 'onRef(_this.flatlistRef)', 'onRef' is an instance of Object)"

I tried

const flatListRef =React.useRef(null);
onRef={ref=>flatListRef}
const myFunc= () => {
flatListRef.scrollToEnd({animated: true});
}

But when I call myFunc, it says "TypeError: flatListRef.scrollToEnd is not a function" because flatListRef is just an object that has the property current set to null.

I tried

const flatListRef =React.useRef(null);
ref={flatListRef}
const myFunc= () => {
console.log(flatList);
}

But when I call myFunc it just freezes my app... MMmmkk....

I tried

let flatListRef;
ref={ref=>{ flatListRef = ref; }}
const myFunc= () => {
console.log(flatList);
}

This also froze/crashed my app.

Can someone provide an example of how the variable is defined in a functional component? Or a whole example on Snack would be great too.. <3

eric-om commented 3 years ago

@ChrisRArendt I don't understand it myself and would appreciate if someone could educate me here, but if you don't define flatListRef (ie let flatListRef;) and just use it in onRef={ref=>{ flatListRef = ref; }}, it should work.

eric-om commented 3 years ago

@nicoglennon did that end up working for you?

nicoglennon commented 3 years ago

I was not able to get it working so have moved on. I might return to it if someone finds a workaround!

MinHyeok90 commented 3 years ago

@eric-om I did what you told me and it works. The problem is, when I save the component and refresh it, onRef is not reloaded, so flatlistRef becomes undefined.

I found a way by useRef. But you probably won't like it.

`

const dragableListContainer = useRef(null);

return (
    <Container>
      {dragableListContainer === undefined ? undefined : (
        <Move
          onPress={() => {
            dragableListContainer.current.flatlistRef.current._component.scrollToIndex(
              {
                animated: true,
                index: 10,
              }
            );
          }}
        />
      )}

      <DraggableFlatList
        ...
        ref={dragableListContainer}
        ....
      />

    </Container>
);

`