react-native-multi-selectbox icon indicating copy to clipboard operation
react-native-multi-selectbox copied to clipboard

Can't use inside a ScrollViews

Open sankaSanjeeva opened this issue 2 years ago • 11 comments

Got this error when I used this inside a ScrollView

VirtualizedLists should never be nested inside plain ScrollViews with the same orientation because it can break windowing and other functionality - use another VirtualizedList-backed container instead.

sankaSanjeeva avatar Aug 20 '21 05:08 sankaSanjeeva

@sankaSanjeeva Did you find any solution to this problem?

nineleaps-shivamsingh avatar Aug 23 '21 04:08 nineleaps-shivamsingh

No, I created my own code.

sankaSanjeeva avatar Aug 23 '21 05:08 sankaSanjeeva

Can you please share it with me?

nineleaps-shivamsingh avatar Aug 23 '21 05:08 nineleaps-shivamsingh

make sure to import style

import React, { useState } from "react";
import { ScrollView, Modal, Text, Pressable, View, TouchableOpacity, Image } from "react-native";
import PropTypes from 'prop-types';
import { Dimensions } from 'react-native';

import styles from '../../Assets/styles/common/multiPicker'

const MultiPicker = ({ items, placeholder, icon, selectedItems, onChangeItems }) => {
    const [visible, setVisible] = useState(false);
    const [selected, setSelected] = useState(selectedItems);

    const windowHeight = Dimensions.get('window').height;

    function selectItems(id) {
        if (selected?.includes(id)) {
            setSelected(selected.filter(x => x != id));
        } else {
            setSelected([...selected, id]);
        }
    }

    function closeModel() {
        onChangeItems(selected);
        setVisible(false);
    }

    return (
        <View>
            <View>
                <Pressable
                    style={styles.inputArea}
                    onPress={() => setVisible(true)
                    }>
                    {icon && (
                        <View style={styles.iconContainer}>
                            <Image source={icon} />
                        </View>
                    )}
                    <Text style={{ paddingLeft: icon ? 0 : 15 }}>{placeholder}</Text>
                </Pressable>
                {
                    selected?.length > 0 && <View style={styles.previewContainer}>
                        {selected?.map((id, index) => (
                            <View key={index} style={styles.previewItem}>
                                <Text>{items?.find(x => x.id == id).name}</Text>
                                <TouchableOpacity onPress={() => {
                                    setSelected(selected.filter(x => x != id));
                                    onChangeItems(selected.filter(x => x != id));
                                }}>
                                    <Image style={styles.closeIcon} source={require('../../Assets/Images/close.png')} />
                                </TouchableOpacity>
                            </View>
                        ))}
                    </View>
                }
            </View>
            <Modal
                animationType="fade"
                transparent={true}
                visible={visible}
                onRequestClose={() => {
                    console.log("Modal has been closed.");
                    setVisible(!visible);
                }}
            >
                <View style={styles.centeredView}>
                    <View style={styles.mainContainer}>

                        <View style={styles.itemsContainer}>
                            <ScrollView style={{ maxHeight: (windowHeight - 200) }}>
                                {
                                    items?.length == 0 ? (
                                        <Text style={{ textAlign: 'center' }}>No items to select</Text>
                                    ) : (
                                        items?.map((item, index) => (
                                            <View
                                                key={index}
                                                style={styles.itemContainer}
                                            >
                                                <TouchableOpacity onPress={() => selectItems(item?.id)} >
                                                    <Text
                                                        style={selected?.includes(item.id) ? { color: '#000' } : { color: '#bbb' }}
                                                    >
                                                        {item.name}
                                                    </Text>
                                                </TouchableOpacity>
                                            </View>

                                        ))
                                    )
                                }
                            </ScrollView>
                        </View>

                        <Pressable
                            style={styles.btnAction}
                            onPress={() => closeModel()}
                        >
                            <Text>{items?.length == 0 ? 'Close' : 'Done'}</Text>
                        </Pressable>
                    </View>
                </View>
            </Modal>
        </View>
    )
}

MultiPicker.propTypes = {
    items: PropTypes.array,
    placeholder: PropTypes.string,
    selectedItems: PropTypes.array,
    onChangeItems: PropTypes.func
}

MultiPicker.defaultProps = {
    items: [],
    placeholder: 'Select',
    selectedItems: []
}

export default MultiPicker;

sankaSanjeeva avatar Aug 23 '21 06:08 sankaSanjeeva

style file

import { StyleSheet } from "react-native";

export default StyleSheet.create({
    btnAction: {
        width: 200,
        height: 50,
        borderRadius: 4,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#f9d132'
    },
    closeIcon: {
        width: 12,
        height: 12,
        marginLeft: 10,
    },
    centeredView: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center"
    },
    iconContainer: {
        width: 55,
        alignItems: 'center'
    },
    inputArea: {
        flexDirection: 'row',
        alignItems: 'center',
        paddingRight: 15,
        paddingVertical: 15,
        backgroundColor: '#ffffff',
        borderColor: '#f0f0f0',
        borderWidth: 1,
        borderRadius: 4,
        elevation: 5,
        shadowColor: '#000',
        shadowOffset: {
            width: 0,
            height: 2
        },
        shadowOpacity: 0.25,
        shadowRadius: 4
    },
    itemContainer: {
        padding: 15,
        borderBottomColor: '#bbb',
        borderBottomWidth: 1
    },
    itemsContainer: {
        marginBottom: 20,
        width: '100%'
    },
    mainContainer: {
        backgroundColor: '#ffffff',
        width: '90%',
        borderRadius: 5,
        padding: 16,
        alignItems: "center",
        shadowColor: '#000',
        shadowOffset: {
            width: 0,
            height: 2
        },
        shadowOpacity: 0.25,
        shadowRadius: 4,
        elevation: 5
    },
    previewContainer: {
        flexDirection: 'row',
        flexWrap: 'wrap'
    },
    previewItem: {
        flexDirection: 'row',
        alignItems: 'center',
        padding: 10,
        marginTop: 16,
        marginHorizontal: 8,
        borderRadius: 20,
        borderColor: '#666',
        borderWidth: 1
    }
});

sankaSanjeeva avatar Aug 23 '21 06:08 sankaSanjeeva

Thank you so much. I will try this out!!

nineleaps-shivamsingh avatar Aug 23 '21 06:08 nineleaps-shivamsingh

and use it with or without an icon

import React, { useState } from 'react';

import MultiPicker from '../../Common/MultiPicker';

const [selectedHorses, setSelectedHorses] = useState([]);

<MultiPicker
        items={allHorses} // arra => [{id: '1', name: 'Apple'}, ...]
        placeholder='Select Horses'
        icon={require('../../../Assets/Images/horse-db.png')}
        selectedItems={selectedHorses} // 
        onChangeItems={setSelectedHorses}
   />

sankaSanjeeva avatar Aug 23 '21 06:08 sankaSanjeeva

i get same issue

hungdev avatar Sep 23 '21 17:09 hungdev

@hungdev You can either go for the solution shared by @sankaSanjeeva and create a separate component. Or there is also a work around that you can use.

<ScrollView nestedScrollEnabled = {true}> <SelectBox listOptionProps={{ nestedScrollEnabled: true }}/> </ScrollView>

This will show you the warning for Virtualized lists but the functionality won't be affected and you will be able to scroll through the list.

nineleaps-shivamsingh avatar Sep 24 '21 05:09 nineleaps-shivamsingh

nestedScrollEnabled works only for Android(https://reactnative.dev/docs/scrollview#nestedscrollenabled-android), How to fix this issue on IOS?

rishiankush avatar Sep 22 '22 14:09 rishiankush

@hungdev You can either go for the solution shared by @sankaSanjeeva and create a separate component. Or there is also a work around that you can use.

<ScrollView nestedScrollEnabled = {true}> <SelectBox listOptionProps={{ nestedScrollEnabled: true }}/> </ScrollView>

This will show you the warning for Virtualized lists but the functionality won't be affected and you will be able to scroll through the list.

work for me with temporary solution in android

import { LogBox } from 'react-native' useEffect(() => {
LogBox.ignoreLogs(["VirtualizedLists should never be nested"]); ...

<ScrollView nestedScrollEnabled = {true}> <SelectBox listOptionProps={{ nestedScrollEnabled: true }}/> </ScrollView>

note: popup ui notif error hidding, function scroll work, but still show error in console. thanks, i hope get permanent fix for this issue with update version react-native-multi-selectbox.

kikikiswanto avatar Oct 06 '22 01:10 kikikiswanto