Search code examples
reactjsreact-nativeandroid-viewpagerreact-navigationreact-native-elements

Auto focus Input within React Native Viewpager


I'm using a React Native Viewpager to take in user entry, and move to the next page on button press. Important to note that moving to the next page happens on button press, and not by normal scrolling, which is disabled.

The best way I could think to handle this was to have a state on the ViewPager, which would propagate into the child Entries.

ViewPager.tsx:

export default function ViewPager({ route, navigation }) {

    const ref: React.RefObject<ViewPager> = React.createRef();
    const [currentPage, setCurrentPage] = useState(0);

    let setEntryPage = (page: number) => {
        ref.current?.setPage(page);
        setCurrentPage(page);
    }


    return (
        <View style={{flex: 1}}>
            <ViewPager
                style={styles.viewPager}
                initialPage={0}
                ref={ref}
                scrollEnabled={false}
            >
                {
                    GlobalStuff.map((entry, index) => {
                        return (
                            <Entry
                                key={index}
                                index={index}
                                pagerFocusIndex={currentPage}
                                pagerLength={quizDeck?.litems.length!}
                                setEntryPage={setEntryPage}
                            />
                        )
                    })
                }
            </ViewPager>
        </View>
    );
};

Entry.tsx:

export function Entry(props: EntryProps) {

    const inputRef: React.RefObject<Input> = React.createRef();
    if (props.pagerFocusIndex === props.index) {
        inputRef.current?.focus();
    }

    return (
        <View>
            <Input
                // ...
                ref={inputRef}
            />
            <IconButton
                icon="arrow-right-thick"
                color={colorTheme.green}
                onPress={() => {
                    props.index !== props.pagerLength - 1 ?
                        props.setEntryPage(props.index + 1) :
                        props.navigation!.reset({ index: 0, routes: [{ name: recapScreenName as any }] });
                }}
            />
// ...

Unfortunately, inputRef appears to be null, and there is probably a better way of achieving what I'm trying to achieve anyway.


Solution

  • Anything in your render loop will be called every time the component renders.

        // This is called on every render
        const inputRef: React.RefObject<Input> = React.createRef();
        
        // So is this, it's always null
        if (props.pagerFocusIndex === props.index) {
            inputRef.current?.focus();
        }
    

    Put side effects in effects.

        // Untested
        const inputRef = useRef();
    
        useEffect(() => {
            if (props.pagerFocusIndex === props.index) {
                inputRef.current?.focus();
            }
        }, [inputRef.current, props.pagerFocusIndex, props.index]);