Search code examples
react-nativeexpocomponentsdry

How to generalize a dynamic list component in React


I am creating an app in React native where I will be using <FlatList> component multiple times.

Now, all the lists are very similar and the only dynamic element is the data inside the lists. The only catch is that the data is an object and for every list the object is different. I am not able to figure out how to make a common function that generates the list and is able to also render specific properties from the data object.

The code below shows the code for most lists and you can see a comment where the dynamic data should be added

function CreateList(props) {
  return (
    <FlatList data={props.data}
    style={{marginBottom: 165}}
    renderItem={({item, index}) => {
      return (
        <TouchableOpacity style={{ paddingVertical: 10, borderBottomWidth: 1, borderBottomColor: "lightgray" }} onPress={() => {
          props.navigation.navigate(props.navigateTo, props.dataToPass)
        }}>
          <View style={styles.tile}>
            {/* {React.cloneElement(props.children, item)} */}
            <View>
              {/* Add dynamic data in a component */}
            </View>
            <MaterialIcons style={{marginRight: 14}} name="keyboard-arrow-right" size={24} color="lightgray" />
          </View>
        </TouchableOpacity>
      );
    }}
    />
  );
}

Is creating a seperate component for all lists a good option here?


Solution

  • You can create a prop for rendering the custom content in renderItem and then use the spread operator to allow further customization other flatlist props (demo)

    export default function GenericList({
      data,
      navigation,
      navigateTo,
      dataToPass,
      renderItemContent,
      // allows customizing list props
      ...flatListProps
    }) {
      return (
        <FlatList
          {...flatListProps}
          data={data}
          style={[{ marginBottom: 165 },flatListProps.style]}
          renderItem={({ item, index }) => {
            return (
              <TouchableOpacity
                style={{
                  paddingVertical: 10,
                  borderBottomWidth: 1,
                  borderBottomColor: 'lightgray',
                }}
                onPress={() => {
                  navigation.navigate(navigateTo, dataToPass);
                }}>
                <View style={styles.tile}>
                  <View>
                    {renderItemContent({item,index})}
                  </View>
                  <MaterialIcons
                    style={{ marginRight: 14 }}
                    name="keyboard-arrow-right"
                    size={24}
                    color="lightgray"
                  />
                </View>
              </TouchableOpacity>
            );
          }}
        />
      );
    }
    

    And then in use:

    <View style={styles.listContainer}>
            <Text style={styles.listTitle}>List A</Text>
            <GenericList
              data={data1}
              renderItemContent={(props) => <RenderItemContent1 {...props} />}
              navigation={navigation}
              navigateTo="Settings"
              dataToPass={someData1}
            />
          </View>
          <View style={styles.listContainer}>
            <Text style={styles.listTitle}>List B</Text>
            <GenericList
              data={data2}
              renderItemContent={(props) => <RenderItemContent2 {...props} />}
              navigation={navigation}
              navigateTo="Settings"
              dataToPass={someData2}
            />
          </View>