react-native-bottom-sheet: [v4] BottomSheetTextInput inside BottomSheetFooter keeps re-rendering, doesn't retain input value

Bug

I am using a BottomSheetModal with a footerComponent to render a text input at the bottom of the sheet. It seems to render okay but when you add text into the BottomSheetTextInput it doesn’t retain the input value. It should set the state of the commentText but it keeps being erased and the keyboard loses focus on each keystroke. I suspect this is because the component keeps re-rendering but I can’t figure out how to resolve this. Are there any guidelines for using this pattern?

Environment info

Library Version
@gorhom/bottom-sheet 4.4.7
react-native 0.70.8
react-native-reanimated 3.2.0
react-native-gesture-handler 2.9.0

Steps To Reproduce

This is my custom footer method which I reference in the footerComponent prop on BottomSheetModal -

const [commentText, setCommentText] = useState('');

const renderActivePostSheetFooter = useCallback(props => (
        <BottomSheetFooter {...props}>
            <View style={styles.footerContainer}>
                <HStack alignItems="center">
                    <BottomSheetTextInput 
                        placeholder="Add your comments" 
                        style={styles.bottomSheetInput}
                        ref={activePostSheetInput}
                        onChangeText={setCommentText}
                        value={commentText}
                    />
                    <Button onPress={addComment}>Post</Button>
                </HStack>
            </View>
        </BottomSheetFooter>
 ), []);

Describe what you expected to happen:

  1. Typing into the BottomSheetTextInput correctly sets the state of commentText.

Reproducible sample code

[<!–

Please use the Bottom Sheet issue snack template https://snack.expo.io/@gorhom/bottom-sheet-v4-reproducible-issue-template or provide a minimal runnable repro as explained above so that the bug can be tested in isolation. or use –>](https://snack.expo.dev/2BcvtOebu)

About this issue

  • Original URL
  • State: closed
  • Created 10 months ago
  • Comments: 17

Most upvoted comments

I got it. The key was to use a ref instead of state to hold the text and use “defaultValue” instead of “value” in the textInput. Here’s what worked for me:


    const inputRef = useRef(null);
    const inputTextRef = useRef('');
    const commentInputState = useState('')
    const footerComponent = useCallback((props) => (
                <BottomSheetFooter {...props}  >
                    <View style={{  paddingHorizontal: 20, }}>
                        <SpacerComponent space='small' />
                        <View style={{ flexDirection: 'row', alignItems: 'center' }}>
                            <BottomSheetTextInput ref={inputRef} 
                                        placeholder='Add a comment...' 
                                        style={{ flex: 1, paddingRight: 5 }} 
                                        onChangeText={(text) => { inputTextRef.current = text; console.log(text) }} 
                                        defaultValue={inputTextRef.current} />
                            <TouchableOpacity
                                onPress={() => {
                                    if (inputTextRef.current !== '') {
                                        putCommentMutation.mutate({ message: inputTextRef.current })
                                        Haptics.impactAsync()
                                        inputTextRef.current = ''
                                        inputRef?.current?.clear();
                                    }
                                }} style={{ flexDirection: 'row' }}>
                                <ComponentVerticalSpacer />
                                <SpacerComponent column space='small' />
                                <IconComponent isLoading={putCommentMutation.isLoading} isTouchable={false} disabled source={require('../../Assets/Icons/Send.png')} />
                                <SpacerComponent column space='small' />
                            </TouchableOpacity>
                        </View>
                        <SpacerComponent />
                    </View>
                </BottomSheetFooter>


            ), [])}
        >

well, try to log the text when change the text and check if u have the updated value ,after that try to pass call backfunction to take the value from the text input

like this

let onChange=(txt)=>{
//must have the updated value
console.log(txt)


 ///Save your text anywhere you like
}

const renderActivePostSheetFooter = useCallback((props) => {
        return (
            <BottomSheetFooter {...props} bottomInset={0}>
                <View>
                        <BottomSheetTextInput 
                            placeholder="Add your comments" 
                            onChangeText={onChange}
                            value={commentText}
                            ref={bottomSheetInput}
                        />
                </View>
            </BottomSheetFooter>
        );
    }, [add ur dep.]);

then if this way not works remove the callback

i hope this solution helps u

I was facing the same problem and realized when you remove the value prop it would stop rerendering. maybe it might help someone. you could also add a ref so you clear the value on submit by using the ref

const [inputValue, setInputValue] = React.useState<string>('')
 const inputRef = React.useRef<typeof BottomSheetTextInput>(null)

 const handleSubmit = () => {
  alert(inputValue);
  inputRef.current?.clear();
}

const renderActivePostSheetFooter = React.useCallback((props) => {
        return (
            <BottomSheetFooter {...props} bottomInset={0}>
                <View>
                        <BottomSheetTextInput 
                            placeholder="Add your comments" 
                            onChangeText={(text) => {
                            console.log(text)
                            setInputValue(text)
                            }}
                            ref={inputRef}
                        />
                </View>
            </BottomSheetFooter>
        );
    }, [inputValue]);