I'm making an app in React Native. There are three components I'm currently concerned with:
AllList.js: A screen comprised of a search bar and a FlatList of RowCard.js instances.
RowCard.js: a custom TouchableHighlight component that displays an item from an API, and displays DrinkPopup.js when tapped by using a state stored in AllList.js.
DrinkPopup.js: A custom Modal component that needs to take an ID from whichever RowCard is tapped and use it to make an API call to get its own data.
I can't figure out how to take the ID from the RowCard or FlatList and pass it to DrinkPopup - how should I do it?
Relevant code for AllList:
export default function AllList() {
const [isLoading, setLoading] = useState(true);
const [drinkData,setDrinkData] = useState([]);
const [searchValue, onChangeText] = useState(''); //needed for search
const [reloading, setReloading] = useState(false);
const [modalVisible, setModalVisible] = useState(false); //normal modal visibility handler
useEffect (() => {
fetch('https://www.thecocktaildb.com/api/json/v1/1/search.php?s=' + searchValue)
.then((response) => response.json())
.then((json) => setDrinkData(json.drinks))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
},[drinkData]);
return (
<View style = {styles.screen}>
<View style = {styles.searchSection}>
<TextInput
placeholder="Search Drinks..."
style={styles.input}
onChangeText={text => onChangeText(text)}
value={searchValue}/>
</View>
<FlatList
data={drinkData}
keyExtractor={({ idDrink }, index) => idDrink}
removeClippedSubviews={true}
initialNumToRender={5}
renderItem={({ item }) => (
<RowCard id={item.idDrink} image={item.strDrinkThumb} title={item.strDrink} alcontent={item.strAlcoholic}
ingredient1={item.strIngredient1} ingredient2={item.strIngredient2} ingredient3={item.strIngredient3} setModalVisible={setModalVisible}
/>
)}
extraData={reloading}
/>
<DrinkPopup modalVisible={modalVisible} setModalVisible={setModalVisible}/>
</View>
);
};
Relevant code for RowCard:
const RowCard = (props) => {
return(
<TouchableHighlight
style={styles.rowCard}
activeOpacity={0.6}
underlayColor={"white"}
onPress={() => {props.setModalVisible(true) }}
>
<View style={styles.rowCard}>
<Image source={{uri: props.image, width: 150, height: 150}} />
<View style={styles.textBox}>
<Text style={styles.titleText}>{props.title}</Text>
<Text style={styles.ingredient}> Main ingredients: {props.ingredient1}, {props.ingredient2}, {props.ingredient3} </Text>
</View>
</View>
</TouchableHighlight>
)
};
Relevant code for DrinkPopup:
const DrinkPopup = (props) => {
return(
<Modal isVisible={props.modalVisible}
onBackdropPress={()=>{props.setModalVisible(false)}} //allows closing modal by tapping outside it or back button
onBackButtonPress={()=>{props.setModalVisible(false)}}
animationIn={"slideInUp"}>
<View style={styles.infocard}>
<View style={styles.titleBox}>
<Text style={styles.header}>I HAVE NO IDEA WHAT YOU PICKED</Text>
</View>
</View>
</Modal>
)
}
Related
I want to pass a value from my parent function to my child function.
but I don't know what to do.
please help advice
The language I use is react native. I want to send search data to main function
this is my code
Main Function
export default function Home() {
return (
<View>
<HeaderHome Upload={Upload} Uri={Uri} />
</View>
);
}
Second Function
export default function HeaderHome({ Upload, Uri }) {
const navigation = useNavigation();
const [showScrollView, setShowScrollView] = useState(false);
const [search, setSearch] = useState('');
const onPress = () => {
setShowScrollView(!showScrollView);
};
console.log(search);
return (
<View>
{showScrollView ? (
<View>
<TextInput
placeholder="Search..."
placeholderTextColor="#000"
onChangeText={(e) => setSearch(e)}
/>
<TouchableOpacity onPress={() => onPress()}>
<Text>Cancel</Text>
</TouchableOpacity>
</View>
) : (
<View>
<View>
<Ionicons
name="md-search"
style={styles.iconSearch}
onPress={() => onPress()}
/>
<Ionicons
name="person-circle"
onPress={() => navigation.navigate('Menu', { Upload, Uri })}
/>
</View>
</View>
)}
</View>
);
}
Create a callback function that you pass as a prop from Home to HeaderHome. This could look as follows.
export default function Home() {
const [search, setSearch] = useState('')
return (
<View>
<HeaderHome setHomeSearch={setSearch} Upload={Upload} Uri={Uri} />
</View>
);
}
In HeaderHome you can call that function in the onPress function and set the state search in the Home component as follows.
export default function HeaderHome({ Upload, Uri, setHomeSearch }) {
const navigation = useNavigation();
const [showScrollView, setShowScrollView] = useState(false);
const [search, setSearch] = useState('');
const onPress = () => {
setShowScrollView(!showScrollView);
};
console.log(search);
const onSearchSet = (text) => {
setSearch(text)
setHomeSearch(text)
}
return (
<View>
{showScrollView ? (
<View>
<TextInput
placeholder="Search..."
placeholderTextColor="#000"
onChangeText={(e) => onSearchSet(e)}
/>
<TouchableOpacity onPress={() => onPress()}>
<Text>Cancel</Text>
</TouchableOpacity>
</View>
) : (
<View>
<View>
<Ionicons
name="md-search"
style={styles.iconSearch}
onPress={() => onPress()}
/>
<Ionicons
name="person-circle"
onPress={() => navigation.navigate('Menu', { Upload, Uri })}
/>
</View>
</View>
)}
</View>
);
}
Here I want to setName of parent component's state from child component while onCHangeText but it gives me "TypeError: setName is not a function. (In 'setName(text)', 'setName' is undefined)" error
Here is my Parent Component
const ProfileCreationScreen = () => {
const [name, setName] = useState()
const userSignup = async () => {
try {
firestore().collection('users').doc("androiduser_mobile89").set({
name: name,
});
alert("Succesfully Created")
} catch (error) {
alert(error);
}
};
return (
<SafeAreaView>
<Text>Create your account</Text>
<Button
mode="contained"
onPress={() => userSignup()}>
Sign Up
</Button>
<View style={{ display: "none" }}>
<NameScreen setName={setName} />
</View>
</SafeAreaView>
)
}
Here is my child component
export const NameScreen = ({ setName, navigation }) => {
return (
<KeyboardAvoidingView
enabled
behavior="padding"
style={styles.container}>
<View>
<Text style={styles.text}>Full Name</Text>
<TextInput
style={styles.textField}
label="Enter your Full Name"
underlineColor="#FF0074"
outlineColor="red"
value={text}
onChangeText={(text) => setName(text)}
/>
<Button mode="contained" style={styles.btn} onPress={() => alert(text)}>
Next
</Button>
</View>
</KeyboardAvoidingView>
);
I'm using react native to create a simple to-do app using Firebase. I used the firestore database. I am able to push the data into Firebase using user inputs. I also tried fetching the data in my screen but I cannot see the data.
dashboard.js ( I want the list of data to be shown here)
export default function Dash(){
const [task, setTask] = useState();
const [taskItems, setTaskItems] = useState([]);
const [loading, setLoading] = useState(true);
const todoCollection = db.collection('todos');
useEffect(() => {
return todoCollection.onSnapshot((querySnapshot) =>{
const list=[];
querySnapshot.forEach(doc => {
const {text, done} = doc.data();
list.push({
id: doc.id,
text,
done,
});
});
setTask(list);
if (loading) {
setLoading(false);
}
});
}, [])
async function addTask() {
await todoCollection.add({
text:task,
done: false,
}).then(()=> {
alert ("Task added Succesfully")
})
setTask('');
Keyboard.dismiss();
setTask(null);
}
const handleTask =()=>{
Keyboard.dismiss();
setTaskItems([...taskItems, task])
setTask(null);
}
const deleteTask = (index) => {
let itemsCopy = [...taskItems];
itemsCopy.splice(index,1);
setTaskItems(itemsCopy);
}
if (loading) {
return null;
}
return(
<>
<View style = {styles.tasksWrapper}>
<Text style={styles.sectionTitle} > ToDos List </Text>
<View style={{height:30}}></View>
<View style = {styles.items}>
{/* {
taskItems.map((item, index)=> {
return(
<Task text={item} del={deleteTask} i={index} />
)
})
} */}
<FlatList
style={{flex: 1}}
data={task}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <Todo {...item} />}
/>
</View>
</View>
<KeyboardAvoidingView behavior={Platform.OS ==="ios"? "padding": "height"}
style={styles.writeTaskWrapper}>
<TextInput style={styles.input} placeholder={'write a task'} value={task} onChangeText={text => setTask(text)}/>
<TouchableOpacity onPress={()=>addTask()}>
<View style={styles.addWrapper}>
<Text style={styles.addText}> + </Text>
</View>
</TouchableOpacity>
</KeyboardAvoidingView>
</>
)
}
Todo.js
import React from 'react';
import {db} from '../firebase/fire';
import { List } from 'react-native-paper';
function Todo({ id, text, done }) {
async function toggleComplete() {
await db
.collection('todos')
.doc(id)
.update({
done: !done,
});
}
return (
<List.Item
text={text}
onPress={() => toggleComplete()}
left={props => (
<List.Icon {...props} icon={done ? 'check' : 'cancel'} />
)}
/>
);
}
export default React.memo(Todo);
The app shows no error but the list is not rendering.
I wasn't able to understand why it does not render the contents of dummyData when I use it with renderItem arrow function but it works with when I pass {item}) => <Text style={styles.item}>{item.key}</Text> to renderItem props directly
const HomeScreen = ({ navigation }) => {
const renderItem = ({item}) => {
<Text style={styles.item}>{item.key}</Text>
}
dataUtils.fetchData();
return(
<View style={styles.container}>
<FlatList
data={dummyData}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</View>
);
};
You are missing a return statement. Delete brackets or add a return.
Like this:
const renderItem = ({item}) =>
<Text style={styles.item}>{item.key}</Text>
Or:
const renderItem = ({item}) => {
return <Text style={styles.item}>{item.key}</Text>
}
In my Todo App i have sucessfully implemented the add and delete functions but the update function is having trouble. What i need it to do is when i click the touchable opacity of a Todo, it should appear in my inputbox and if any change is made then that todo should be updated e.g clicking on abcd must make it appear in input box and changes made to it should be updated. Picture is also added below
export default function Todo() {
const [getText, setText] = useState('');
const [getList, setList] = useState([]);
const addItem = () => {
setList([...getList, {key: Math.random().toString(), data: getText}]);
setText('');
}
const removeItem = (itemKey) => {
setList(() => getList.filter(item => item.key != itemKey));
}
const updateFieldChanged = (index) => e => {
let newArr = [...getList]; // copying the old datas array
newArr[index] = e.target.value; // replace e.target.value with whatever you want to change it to
setList(newArr);
}
return (
<View style={styles.container}>
<Text style={styles.title}>todo</Text>
<View style={styles.inputContainer}>
<TextInput
style={styles.textInput}
placeholder="Enter Item"
onChangeText={text => setText(text)}
value={getText}
/>
<CustomButton
text = 'add'
color='red'
title= 'add'
textSize={20}
textColor="white"
onPressEvent={addItem}
/>
</View>
<ScrollView style={styles.scrollview}>
{getList.map((item, id) =>
<TouchableOpacity
key={item.key}
activeOpacity={0.7}
onPress= {() => updateFieldChanged(id)}
>
<View style={styles.scrollviewItem}>
<Text style={styles.scrollviewText}>{id}) {item.data}</Text>
<TouchableOpacity
onPress={() => removeItem(item.key)}
>
<View style={styles.crosstextcontainer}>
<Text style={styles.crosstext}>X</Text>
</View>
</TouchableOpacity>
</View>
</TouchableOpacity>
)}
</ScrollView>
</View>
);
}
Change
<TouchableOpacity
key={item.key}
activeOpacity={0.7}
onPress= {() => updateFieldChanged(id)}
>
to
<TouchableOpacity
key={item.key}
activeOpacity={0.7}
onPress= {() => updateFieldChanged(id,getText)}
>
Here iam passing the text that you need to enter to update a particular field
change your updateFieldChanged like this:
const updateFieldChanged = (index, text) => {
let newArr = [...getList]; // copying the old datas array
newArr[index].data = text; // replace e.target.value with whatever you want to change it to
setList(newArr);
setText('');
}
Here iam assigning the text you entered in the TextInput to the data object, which will update the array.
Hope this helps!