I have seen a lot of answers for FlatList not scrolling for React Native, but none of the answers seem to work.
The answer usually says to add flex: 1 to the parent, but here is an example that has that and it still doesn't scroll.
You can test here, but make sure you select iOS or Android. It does scroll for web.
import React from 'react';
import { SafeAreaView, View, FlatList, StyleSheet, Text, StatusBar } from 'react-native';
const DATA = [
{
id: "bd7acbea-c1b1-46c2-aed5-3ad53abb28ba",
title: "First Item",
},
{
id: "3ac68afc-c605-48d3-a4f8-fbd91aa97f63",
title: "Second Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d72",
title: "Third Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d73",
title: "Fourth Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d74",
title: "Fifth Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d75",
title: "Sixth Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d76",
title: "Seventh Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d77",
title: "Eighth Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d77",
title: "Ninth Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d77",
title: "Tenth Item",
},
];
const Item = ({ title }) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const App = () => {
const renderItem = ({ item }) => (
<Item title={item.title} />
);
return (
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
},
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 32,
},
});
export default App;
Modify the SafeAreaView to be within it's own container and added a View container with flexGrow: 1 instead of flex: 1:
const App = () => {
return (
<SafeAreaView style={styles.safe}>
<View style={styles.container}>
<FlatList
data={data}
renderItem={({ item }) => (<Item title={item.title} />) }
keyExtractor={item => item.id}
/>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safe: {
marginTop: StatusBar.currentHeight || 0,
},
container: {
flexGrow: 1
},
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 32,
},
});
Above worked in a snack Expo testing for iOS. Entire sample code with a little re-write:
import React from 'react';
import { SafeAreaView, View, FlatList, StyleSheet, Text, StatusBar } from 'react-native';
const data = [
{
id: "bd7acbea-c1b1-46c2-aed5-3ad53abb28ba",
title: "First Item",
},
{
id: "3ac68afc-c605-48d3-a4f8-fbd91aa97f63",
title: "Second Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d72",
title: "Third Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d73",
title: "Fourth Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d74",
title: "Fifth Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d75",
title: "Sixth Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d76",
title: "Seventh Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d77",
title: "Eighth Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d77",
title: "Ninth Item",
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d77",
title: "Tenth Item",
},
];
const Item = ({ title }) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const App = () => {
return (
<SafeAreaView style={styles.safe}>
<View style={styles.container}>
<FlatList
data={data}
renderItem={({ item }) => (<Item title={item.title} />) }
keyExtractor={item => item.id}
/>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safe: {
marginTop: StatusBar.currentHeight || 0,
},
container: {
flexGrow: 1
},
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 32,
},
});
export default App
Related
I got array:
const DATA_Section = [
{
id: 0,
text: "Some text1",
},
{
id: 1,
text: "Some text2",
},
{
id: 2,
text: "Some text 3",
},
];
and I want to use text from this array to create dynamic component. Idea is: I can see text[0], I click some button then I can see text[1], something like loop. How can to do it?
You can do this way maybe.
const DATA_Section = [
{
id: 0,
text: "Some text1",
},
{
id: 1,
text: "Some text2",
},
{
id: 2,
text: "Some text 3",
},
];
const Component: FC = () => {
const [initialRenderState, setInitialRenderState] =
useState(DATA_Section[0])
return (
<View>
{intialRenderState.map(item => <Text key={item.id}>{item.text}</Text>)}
<Pressable onPress={()=> setInitialRenderState(prevState => [...prevState, Data_Section[prevState.length]])}>
<Text>Add item</Text>
<Pressable>
</View>
)
}
const DATA_Section = [
{
id: 0,
text: "Some text1",
},
{
id: 1,
text: "Some text2",
},
{
id: 2,
text: "Some text 3",
},
];
const sample = () => {
const [ index, setIndex ] = useState(0);
const handleChangeIndex = () => {
if (index < DATA_Section.length) {
setIndex(index + 1);
}else{
setIndex(0);
}
}
return(
<View style={{flex:1}}>
<TouchableWithoutFeedback onPress={()=>handleChangeIndex()}>
<View style={{width:100, height:50, backgroundColor:'red'}}>
<Text>
{DATA_Section[index].text}
</Text>
</View>
</TouchableWithoutFeedback>
</View>
);
}
I'm trying to keep track of a list of chosen exercises, and then render 'added' if the chosen exercise is in the list, or 'not added' if it's not in the list. I add the exercise to chosenExerciseArray when it's not already included, and remove it if it is. When I use console.log(chosenExerciseArray) it correctly displays the list, but if I use setChosenExercises(chosenExerciseArray) the state does not get updated correctly, and the chosenExerciseArray somehow stops logging the correct array as well.
I've also tried adding chosenExerciseArray as a useEffect dependency, but that causes an infinite loop. I'm really not sure what's going on here, but I believe it's an issue with my understanding of state.
EDIT: The TouchableOpacity is inside of a map, which may also be the issue, but I'm not sure how
<View style={styles.exerciseheadericongroup}>
<TouchableOpacity
onPress={() => {
if(!chosenExerciseArray.includes(exercise.id)) {
chosenExerciseArray.push(exercise.id);
} else {
for(var i=0; i<chosenExerciseArray.length; i++) {
if(chosenExerciseArray[i] === exercise.id) {
chosenExerciseArray.splice(i, 1);
}
}
}
console.log(chosenExerciseArray);
}}
>
{chosenExercises.includes(exercise.id) ? (
<Text>added</Text>
) : (
<Text>not added</Text>
)}
</TouchableOpacity>
</View>
As stated in the React documentation
state is a reference to the component state at the time the change is being applied. It should not be directly mutated. Instead, changes should be represented by building a new object based on the input from state and props. [link]
I wish I could explain it better, but basically it means that manipulating chosenExerciseArray with .push and then executing setChosenExerciseArray(chosenExerciseArray) messes up the state handling. You need to make a copy of chosenExerciseArray and manipulate it instead, and then use the manipulated copy to set the new state. I have created two examples for you to look at:
Example 1 uses a different logic, where added is part of the exercise object itself, such that that the chosenExerciseArray is not necessary to keep track of whether the exercise is added or not. This is also a bit more efficient, because you do not need to check if an exercise is in the chosenExerciseArray or not.
import React, { useState } from "react";
import { Pressable, StyleSheet } from "react-native";
import { Text, View } from "../components/Themed";
interface Exercise {
id: string;
name: string;
added: boolean;
}
const updateExerciseArray = (exercises: Exercise[], exercise: Exercise) => {
// Shallow copy
const updatedExercises = [...exercises];
// Manipulate the copy
return updatedExercises.map((updatedExercise) => {
if (updatedExercise.id === exercise.id) {
// Shallow copy of object in array where added is negated
return { ...updatedExercise, added: !updatedExercise.added };
}
return updatedExercise;
});
};
export default function Example1() {
const [exercises, setExercises] = useState<Exercise[]>([
{ id: "1", name: "Pushups", added: false },
{ id: "2", name: "Situps", added: false },
{ id: "3", name: "Squats", added: false },
{ id: "4", name: "Pullups", added: false },
{ id: "5", name: "Leg Raises", added: false },
{ id: "6", name: "Plank", added: false },
{ id: "7", name: "Burpees", added: false },
{ id: "8", name: "Jumping Jacks", added: false },
{ id: "9", name: "Wall Sit", added: false },
]);
return (
<View style={styles.container}>
{exercises.map((exercise) => {
return (
<Pressable
key={exercise.id}
onPress={() => {
setExercises(updateExerciseArray(exercises, exercise));
}}
style={styles.row}
>
<Text style={styles.text}>{exercise.name}</Text>
<Text style={styles.text}>
{exercise.added ? "Added" : "Not added"}
</Text>
</Pressable>
);
})}
</View>
);
}
const styles = StyleSheet.create({
container: {
paddingHorizontal: 50,
flex: 1,
backgroundColor: "#fff",
justifyContent: "center",
},
row: {
marginVertical: 10,
flexDirection: "row",
justifyContent: "space-between",
},
text: {
fontSize: 20,
},
});
Example 2 uses a solution that fits your current implementation.
import React, { useState } from "react";
import { Pressable, StyleSheet } from "react-native";
import { Text, View } from "../components/Themed";
interface Exercise {
id: string;
name: string;
}
const updateExerciseArray = (
chosenExerciseArray: string[],
exerciseToChange: Exercise
) => {
// Shallow copy
const updatedChosenExerciseArray = [...chosenExerciseArray];
if (updatedChosenExerciseArray.includes(exerciseToChange.id)) {
// Remove the exercise from the array
return updatedChosenExerciseArray.filter(
(exerciseId) => exerciseId !== exerciseToChange.id
);
} else {
// Append the exercise to the array
return [...updatedChosenExerciseArray, exerciseToChange.id];
}
};
export default function Example2() {
const [exercises] = useState<Exercise[]>([
{ id: "1", name: "Pushups" },
{ id: "2", name: "Situps" },
{ id: "3", name: "Squats" },
{ id: "4", name: "Pullups" },
{ id: "5", name: "Leg Raises" },
{ id: "6", name: "Plank" },
{ id: "7", name: "Burpees" },
{ id: "8", name: "Jumping Jacks" },
{ id: "9", name: "Wall Sit" },
]);
const [chosenExerciseArray, setChosenExerciseArray] = useState<string[]>([
"1",
"2",
]);
return (
<View style={styles.container}>
{exercises.map((exercise) => {
return (
<Pressable
key={exercise.id}
onPress={() => {
setChosenExerciseArray(
updateExerciseArray(chosenExerciseArray, exercise)
);
}}
style={styles.row}
>
<Text style={styles.text}>{exercise.name}</Text>
<Text style={styles.text}>
{chosenExerciseArray.includes(exercise.id)
? "Added"
: "Not added"}
</Text>
</Pressable>
);
})}
</View>
);
}
const styles = StyleSheet.create({
container: {
paddingHorizontal: 50,
flex: 1,
backgroundColor: "#fff",
justifyContent: "center",
},
row: {
paddingVertical: 10,
flexDirection: "row",
justifyContent: "space-between",
},
text: {
fontSize: 20,
},
});
export default class App extends Component {
constructor() {
super();
// flatlist data
this.state = {
data: [
{ key: "Skptricks", data: "one", name: "one" },
{ key: "Sumit" , data: "two" , name: "one"},
{ key: "Amit" , data: "three", name: "one"},
],
}
}
// flat list
render() {
return (
<View>
<FlatList
data={this.state.data}
renderItem={({item}) => <Text>{item.name}</Text>
/>
</View>
);
}
}
Can you explain what exactly do you need to print ? Please refer the code. I'm printing the 'name' item of your array. If you want to print 'data' items , use item.data inside renderItem. Let me know if you need any help.
const DATA = [{ key: "Skptricks", data: "one", name: "one" },
{ key: "Sumit" , data: "two" , name: "one"},
{ key: "Amit" , data: "three", name: "one"},
];
const Item = ({ name }) => (
<View style={styles.item}>
<Text style={styles.name}>{name}</Text>
</View>
);
const App = () => {
const renderItem = ({ item }) => (
<Item name={item.name} />
);
return (
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
},
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
name: {
fontSize: 32,
},
});
As the title says I want to achieve the following model,I have a local imported json file, and I want user to write on the search bar,according to the text user writes,I want the json to get filtered and show it at the bottom area.
My json object:
export const cards = [
{
img: 'https://media.emailonacid.com/wp-content/uploads/2019/05/EOA_MediaQueries2019_Blog.jpg',
author: null,
description: 'Liked Songs'
},
{
img: 'https://i.scdn.co/image/ab67706c0000da845f2575c6513aa8e264879e7d',
author: null,
description: 'Essa Gelada Eu vou beber'
},
{
img: 'https://vintageculture.com/wp-content/uploads/2020/04/Slow-Down-VC-e-Slow-Motion-Remix.jpg',
author: null,
description: 'Slow Down (feat. Jhon)'
},
{
img: 'https://i.pinimg.com/474x/18/a3/b2/18a3b25cf87f439a5c0bc0fa7ae0ca54.jpg',
author: null,
description: 'Marilia Mendonça'
},
{
img: 'https://i.scdn.co/image/ab67616d0000b273d77d08d4bb06171ce0fe2a0e',
author: null,
description: 'Jorge & Mateus'
},
{
img: 'https://i.scdn.co/image/ab67616d0000b2738ddd20bf644f1d50adafad7b',
author: null,
description: 'All Around The World'
},
]
export const recentlys = [
{
img: 'https://i.scdn.co/image/ab67616d0000b2738ddd20bf644f1d50adafad7b',
author: 'R3AB',
description: 'All Around The World'
},
{
img: 'https://vintageculture.com/wp-content/uploads/2020/04/Slow-Down-VC-e-Slow-Motion-Remix.jpg',
author: 'Vinatge Culture',
description: 'Slow Down (feat. Jhon)'
},
{
img: 'https://i.pinimg.com/474x/18/a3/b2/18a3b25cf87f439a5c0bc0fa7ae0ca54.jpg',
author: 'Marilia Mendonça',
description: 'Marilia Mendonça'
},
{
img: 'https://i.scdn.co/image/ab67706c0000da845f2575c6513aa8e264879e7d',
author: 'Jorge & Mateus',
description: 'Essa Gelada Eu vou beber'
},
{
img: 'https://i.scdn.co/image/ab67616d0000b273d77d08d4bb06171ce0fe2a0e',
author: 'Jorge & Mateus',
description: 'Jorge & Mateus'
},
]
export const playlists = [
{
img: 'https://vintageculture.com/wp-content/uploads/2020/04/Slow-Down-VC-e-Slow-Motion-Remix.jpg',
author: 'Vinatge Culture',
description: 'Slow Down (feat. Jhon)'
},
{
img: 'https://i.scdn.co/image/ab67616d0000b2738ddd20bf644f1d50adafad7b',
author: 'R3AB',
description: 'All Around The World'
},
{
img: 'https://i.scdn.co/image/ab67706c0000da845f2575c6513aa8e264879e7d',
author: 'Jorge & Mateus',
description: 'Essa Gelada Eu vou beber'
},
{
img: 'https://i.pinimg.com/474x/18/a3/b2/18a3b25cf87f439a5c0bc0fa7ae0ca54.jpg',
author: 'Marilia Mendonça',
description: 'Marilia Mendonça'
},
{
img: 'https://i.scdn.co/image/ab67616d0000b273d77d08d4bb06171ce0fe2a0e',
author: 'Jorge & Mateus',
description: 'Jorge & Mateus'
},
]
export const artists = [
{
img: 'https://i.scdn.co/image/ab67706c0000da845f2575c6513aa8e264879e7d',
author: 'Jorge & Mateus',
description: 'Essa Gelada Eu vou beber'
},
{
img: 'https://i.scdn.co/image/ab67616d0000b2738ddd20bf644f1d50adafad7b',
author: 'R3AB',
description: 'All Around The World'
},
{
img: 'https://vintageculture.com/wp-content/uploads/2020/04/Slow-Down-VC-e-Slow-Motion-Remix.jpg',
author: 'Vintage Culture',
description: 'Slow Down (feat. Jhon)'
},
{
img: 'https://i.scdn.co/image/ab67616d0000b273d77d08d4bb06171ce0fe2a0e',
author: 'Jorge & Mateus',
description: 'Jorge & Mateus'
},
{
img: 'https://i.pinimg.com/474x/18/a3/b2/18a3b25cf87f439a5c0bc0fa7ae0ca54.jpg',
author: 'Marilia Mendonça',
description: 'Marilia Mendonça'
},
{
img: 'https://i.scdn.co/image/1fd90cd274102d458ec3a9243ced32e659738133',
author: 'Calvin Harris',
description: 'Your Daily Mix 1'
},
{
img: 'https://i.scdn.co/image/5e79a3aa31ed34c951b607e26c0df690c0118fc8',
author: 'KVSH',
description: null
},
{
img: 'https://i1.sndcdn.com/avatars-000494458926-ftqfqe-t500x500.jpg',
author: 'R3AB',
description: null
},
{
img: 'https://i.scdn.co/image/82e6e7e4f318d0b2ba80b09d00db2552966f2fdc',
author: 'Liu',
description: 'Your Daily Mix 2'
},
{
img: 'https://i.scdn.co/image/ab67706f000000026cac5751f488923a32f4ee79',
author: 'Martin Garrix',
description: null
},
{
img: 'https://penews.com.br/wp-content/uploads/2019/10/vintge-e1571597522273.jpg',
author: 'Vintage Culture',
description: null
},
{
img: 'https://i.scdn.co/image/339b4f2b8fa072d2130ad69cd603919e6279258e',
author: 'ALOK',
description: null
},
]
below is the exact code that I have written and where I want to implement the function:
import React, {useState} from 'react';
import {
SafeAreaView,
ScrollView,
View,
Text,
TextInput,
StyleSheet,
Dimensions,
} from 'react-native';
import {TabView, SceneMap} from 'react-native-tab-view';
import Icon from 'react-native-vector-icons/FontAwesome';
import {cards} from '../../Data/Home';
const FirstRoute = () => <View style={[styles.scene]} />;
const SecondRoute = () => <View style={[styles.scene]} />;
const initialLayout = {width: Dimensions.get('window').width};
export default function Search() {
const [search, setSearch] = useState({});
const [index, setIndex] = React.useState(0);
const [tmpCards] = useState(cards);
const [routes] = React.useState([
{key: 'first', title: 'Users'},
{key: 'second', title: 'Videos'},
]);
const renderScene = SceneMap({
first: FirstRoute,
second: SecondRoute,
});
const filteredData = () => {
tmpCards.filter((user) => {
console.log(
tmpCards.description.toLowerCase().includes(search.toLowerCase()),
);
});
};
return (
<SafeAreaView style={{flex: 1, backgroundColor: '#fff'}}>
<View style={{padding: 10}}>
<View style={{flexDirection: 'row', backgroundColor: '#D3D3D3'}}>
<Icon
name="search"
size={20}
style={{marginTop: 12, marginLeft: 5}}
/>
<TextInput
placeholder="Search"
value={search}
onChangeText={(search) => setSearch(search)}
style={{marginLeft: 10}}
/>
{filteredData.map((user, id) => {
<Text>{tmpCards.description}</Text>;
})}
</View>
</View>
<TabView
navigationState={{index, routes}}
renderScene={renderScene}
onIndexChange={setIndex}
initialLayout={initialLayout}
style={{backgroundColor: '#fff'}}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
scene: {
flex: 1,
},
});
But am getting this error:
TypeError: undefined is not a function (near '...filteredData.map...')
Any suggestion would be helpful.
You have couple of issue with your code. fitlerdata method is not returning anything also filter function implementation is wrong as per your requirements. I have modified your code a bit. Please try that now.
import React, { useState } from 'react';
import {
SafeAreaView,
ScrollView,
View,
Text,
TextInput,
StyleSheet,
Dimensions,
} from 'react-native';
import { TabView, SceneMap } from 'react-native-tab-view';
import Icon from 'react-native-vector-icons/FontAwesome';
import { cards } from '../../Data/Home';
const FirstRoute = () => <View style={[styles.scene]} />;
const SecondRoute = () => <View style={[styles.scene]} />;
const initialLayout = { width: Dimensions.get('window').width };
export default function Search() {
const [search, setSearch] = useState({});
const [index, setIndex] = React.useState(0);
const [tmpCards] = useState(cards);
const [routes] = React.useState([
{ key: 'first', title: 'Users' },
{ key: 'second', title: 'Videos' },
]);
const renderScene = SceneMap({
first: FirstRoute,
second: SecondRoute,
});
const filteredData = () => tmpCards.filter((user) => user.description.toLowerCase().includes(search.toLowerCase()))
return (
<SafeAreaView style={{ flex: 1, backgroundColor: '#fff' }}>
<View style={{ padding: 10 }}>
<View style={{ flexDirection: 'row', backgroundColor: '#D3D3D3' }}>
<Icon
name="search"
size={20}
style={{ marginTop: 12, marginLeft: 5 }}
/>
<TextInput
placeholder="Search"
value={search}
onChangeText={(search) => setSearch(search)}
style={{ marginLeft: 10 }}
/>
{filteredData().map((user, id) =><Text>{user.description}</Text>)}
</View>
</View>
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
initialLayout={initialLayout}
style={{ backgroundColor: '#fff' }}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
scene: {
flex: 1,
},
});
I'm trying to loop over a simple array of objects and display the content inside Text tag. The looping is being done but the actual content is not displayed on the screen.
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
export default class App extends React.Component {
render() {
const initialArr = [
{
id: 1,
color: "blue",
text: "text1"
},
{
id: 2,
color: "red",
text: "text2"
},
{
id: 3,
color: "green",
text: "text3"
}
];
return (
<View style={styles.container}>
{initialArr.map((prop, key) => {
return (
<Text key={key}>{prop[1]}</Text>
);
})}
</View>
);
}
}
render() {
const initialArr = [
{ id: 1, color: "blue", text: "text1" },
{ id: 2, color: "red", text: "text2" },
{ id: 3, color: "green", text: "text3"}
];
var textInputComponents = initialArr.map((prop, key) => {
return (
<Text key={key}>{prop.text}</Text>
);
})
return (
<View style={styles.container}>
{textInputComponents}
</View>
);
}
Apart from John_mc's answer, instead of using inline mapping, you can also split the mapping operation, to improve readability and maintainability