I'm rendering a few items in my map in ContactListand upon clicking on the thumbnail, I want to navigate to a new screen UserDetailsScreen such that the data about the clicked item is also passed along.
Previously I was using modals, but now I trying to switch to react-navigation.
ContactList.tsx:
export const ContactList: React.FunctionComponent<UserProps> = ({
data,
onDeleteContact,
}) => {
const [isUserVisible, setIsUserVisible] = useState(false);
//const [visibleUser, setVisibleUser] = useState<any>();
const navigation = useNavigation();
return (
<View style={styles.users}>
{data.users.nodes[0].userRelations.map(
(item: { relatedUser: RelatedUser; id: number }) => {
const numberOfFriends = item.relatedUser.userRelations.length;
const numberPlate = 'WHV AB 123';
return (
<View style={styles.item} key={item.id}>
{/* <TouchableOpacity onPress={() => setIsUserVisible(true)}> */}
<TouchableOpacity
onPress={() =>
navigation.navigate('UserDetailsScreen', {
firstName: item.relatedUser.firstName,
rating: item.relatedUser.rating,
numberOfFriends: numberOfFriends,
onDeleteContact: onDeleteContact,
isUserVisible: isUserVisible,
setIsUserVisible: setIsUserVisible,
numberPlate: numberPlate,
navigation: navigation,
})
}>
<Thumbnail
}}></Thumbnail>
</TouchableOpacity>
<View style={styles.nameNumber}>
<Text style={styles.userName}>{userName}</Text>
</View>
{/* <UserDetails
firstName={item.relatedUser.firstName}
rating={item.relatedUser.rating}
numberOfFriends={numberOfFriends}
onDeleteContact={onDeleteContact}
isUserVisible={isUserVisible}
setIsUserVisible={setIsUserVisible}
numberPlate={numberPlate}>
</UserDetails> */}
</View>
);
},
)}
</View>
);
};
UserDetailsScreen:
export const UserDetailsScreen: React.FunctionComponent<UserProps> = ({
firstName,
rating,
numberOfFriends,
numberPlate,
onDeleteContact,
navigation,
// isUserVisible,
// setIsUserVisible,
}) => {
//const navigation = useNavigation();
const fName = navigation.getParam('firstName')
return (
// <Modal visible={isUserVisible}>
<View style={styles.container}>
<View>
<TouchableOpacity
style={styles.cross}
//onPress={() => setIsUserVisible(false)}>
onPress={() => navigation.navigate('Whitelist')}>
<Thumbnail></Thumbnail>
</TouchableOpacity>
</View>
<View style={styles.searchLocationContainer}>
<UserInfoContainer
firstName={firstName}
rating={rating}
numberPlate={numberPlate}
numberOfFriends={numberOfFriends}></UserInfoContainer>
</View>
</View>
// </Modal>
);
};
Similarly, when I click on the thumbnail on this screen, I want to go back to my previous page where I can click on another object.
However, I keep getting an error that navigation.getParam is undefined. How can I fix this?
Hi you will get the data sent in the navigation params in
props.route.params
Related
help, so lets say i got a bunch of data from an API (in Homescreen.js), which then i send to a component called "Artikel.js", how should i send the data in each of these articles to a screen called "DetailScreen.js". please someone help me, i'd appreciate it very very much, thanks in advance and sorry for bad english
const Homescreen = () => {
const [articles, setArticles] = useState([]);
const getArticles = () => {
axios
.get(
"https://newsapi.org/v2/top-headlines?country=us&apiKey=API_KEY",
{
params: {
category: "technology",
},
}
)
.then((response) => {
setArticles(response.data.articles);
})
.catch(function (error) {
console.log(error);
})
.then(function () {});
};
useEffect(() => {
getArticles();
}, []);
return (
<SafeAreaView style={styles.container}>
<FlatList
data={articles}
renderItem={({ item }) => (
<Artikel
urlToImage={item.urlToImage}
title={item.title}
description={item.description}
author={item.author}
publishedAt={item.publishedAt}
sourceName={item.source.name}
url={item.url}
/>
)}
keyExtractor={(item) => item.title}
/>
</SafeAreaView>
);
};
export default Homescreen;
Artikel.js
const Artikel = (props) => {
const navigation = useNavigation();
const goToDetail = () => {
navigation.navigate("Detail", {judul: 'Asu'});
};
// const goToSource = () => {
// WebBrowser.openBrowserAsync(props.url);
// };
return (
<SafeAreaView style={styles.container}>
<Pressable onPress={goToDetail}>
<Image
style={styles.image}
on
source={{
uri: props.urlToImage,
}}
/>
</Pressable>
<View style={{ paddingHorizontal: 20, paddingBottom: 10 }}>
<Text style={styles.title}>{props.title}</Text>
<Text style={styles.deskripsi} numberOfLines={3}>
{props.description}
</Text>
<View style={styles.data}>
<Text style={styles.h2}>
source:<Text style={styles.sumber}> {props.sourceName}</Text>
</Text>
<Text style={styles.tanggal}>
{moment(props.publishedAt).format("MMM Do YY")}
</Text>
</View>
</View>
</SafeAreaView>
);
};
export default Artikel;
"DetailScreen.js"
const DetailScreen = (props) => {
return (
<SafeAreaView style={styles.container}>
<Header />
<View style={styles.image}>
<Image
source={{
uri: props.thumbnail,
}}
style={styles.image}
/>
</View>
<View style={styles.bodyartikel}>
<Text style={styles.judul}>PROPS TITLE</Text>
<Text style={styles.artikel}>
PROPS.ARTICLE
</Text>
<View style={styles.footer}>
<Text style={styles.h1}>
By: <Text style={styles.sumber}>Salman</Text>
</Text>
<Text>12 Okt 2020</Text>
</View>
</View>
</SafeAreaView>
);
};
export default DetailScreen;
i tried to make a list of the datas i need in Artikel.js and made it into a list, but it didnt work
So in your Article.js you have called an method to navigate to DetailScreen.js. You have can do like this.
In Article.js:
<Pressable onPress={() => goToDetail(props)}> // pass props as argument
<Image
style={styles.image}
source={{
uri: props.urlToImage,
}}
/>
</Pressable>
now in your goToDetail method:
// Catch passed arguments as props
const goToDetail = (props) => {
navigation.navigate('Details', {
title: props.title,
description: props.description,
})
// As for now just passing title and description from props
};
Now to access Passed data in Detail.js:
import { useRoute } from '#react-navigation/native';
const DetailScreen = () => {
let route = useRoute(); // using route hooks
let {title, data} = route.params
return (
<YourComponent/>
);
};
In this way you can pass data from one screen to another. For more detail you always can visit react native navigation docs: https://reactnavigation.org/docs/params
I have a parent and nest child component hierarchy of QuestionsAndAnswersScreen -> QuestionInput -> QuestionSelector -> AnswerSelector. I need to pass the question and answer object back up to the QuestionAndAnswerScreen in order to show it on the view. However I cannot find a way without going into deep nested callbacks.
Here is my code for the QuestionAnswerScreen and AnswerSelector:
function QuestionsAndAnswers() {
const {shell, body} = styles;
return (
<View style={shell}>
<SignUpHeader title="Add your answers" page={5}/>
<View style={body}>
{qAndA[1] ? <Answer question={qAndA[1].question} answer={qAndA[1].answer}/> : <QuestionInput />}
{qAndA[2] ? <Answer question={qAndA[2].question} answer={qAndA[2].answer}/> : <QuestionInput />}
{qAndA[3] ? <Answer question={qAndA[3].question} answer={qAndA[3].answer}/> : <QuestionInput />}
</View>
<SignUpFooter
title={`Questions\n& Answers`}
buttonTitle={"Done"}
disabled={false}
route="QuestionsAndAnswers"
/>
</View>
);
}
function AnswerInput(props: AnswerInputProps) {
const {question, visible, answerModalVisible} = props;
const {pickAnAnswer, doneButton, answerTextInput, questionStyle, shell, buttonText} = styles;
const [modalVisible, setModalVisible] = useState(visible)
const [answer, setAnswer] = useState('');
const navigation = useNavigation();
useEffect(() => {
setModalVisible(visible)
}, [visible])
function answerQuestion() {
setModalVisible(false);
navigation.navigate('QuestionsAndAnswers');
}
return (
<View>
<Modal style={shell}
isVisible={modalVisible}
onBackdropPress={() => {
setModalVisible(false);
answerModalVisible(false);
}}
>
<View>
<Text style={pickAnAnswer}>Add answer</Text>
</View>
<View>
<Text style={questionStyle}>{question}</Text>
</View>
<View>
<TextInput
style={answerTextInput}
placeholder="Add your answer here..."
placeholderTextColor="#878787"
onChangeText={(text: string) => setAnswer(text)}
/>
<View style={{alignItems: 'flex-end', marginTop: 44}}>
<TouchableOpacity style={doneButton} onPress={() => answerQuestion()}>
<Text style={buttonText}>
Done
</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
</View>
);
}
As you can see I get my Question and Answer in the AnswerInput but then need to navigate back to the main screen in order to display them. Any help would be great thanks :)
I cannot figure out how to display the word that I am translating with my google-translate API ... I want to display the translated word from function newText to get displayed in the Text tag where I call that function.. (also the word to translate it's coming from the TextInput 'word to translate'
const Main = ({ navigation }) => {
let apiKey = "AIzaasdfasdfasdfasdfasdfsdc";
let googleTranslate = require("google-translate")(apiKey);
const [text, setText] = useState("");
const newText = () => {
googleTranslate.translate(text, "es", function (err, translation) {
return translation.translatedText;
});
};
const onChangeText = (text) => setText(text);
return (
<View style={styles.screen}>
<ImageBackground
source={require("./assets/book.png")}
style={styles.backgroundImage}
>
<View style={styles.innerText}>
<Text style={{ fontSize: 20 }}>Welcome back Elisa, </Text>
<Text>let's practice that pronunciation...</Text>
<TextInput
placeholder="Word to translate"
style={styles.input}
onChangeText={onChangeText}
/>
</View>
<Text style={styles.output}>{newText()}</Text>
<View style={styles.button}>
<Button
title="START"
onPress={() => navigation.navigate("BACK_HOME")}
/>
</View>
</ImageBackground>
</View>
);
};
I've converted newText() into an async function.
You can either call newText() with a onClick event, or use onChange as I've modified your codes below.
Google translate will be called whenever your input change (it's not a good idea because it means if you type 100 characters, it translate 100 times.).
I suggest you to add a button like this.
<button onClick={() => newText(text)}>Translate Me!</button>
const Main = ({ navigation }) => {
let apiKey = "AIzaasdfasdfasdfasdfasdfsdc";
let googleTranslate = require("google-translate")(apiKey);
const [text, setText] = useState("");
const [ translated, setTranslated ] = useState('');
const newText = async (toBeTranslated) => {
await googleTranslate.translate(toBeTranslated, "es", function (err, translation) {
setTranslated(translation.translatedText)
});
};
const onChangeText = (text) => {
setText(text);
//handle translation when text change.
newText(text);
}
return (
<View style={styles.screen}>
<ImageBackground
source={require("./assets/book.png")}
style={styles.backgroundImage}
>
<View style={styles.innerText}>
<Text style={{ fontSize: 20 }}>Welcome back Elisa, </Text>
<Text>let's practice that pronunciation...</Text>
<TextInput
placeholder="Word to translate"
style={styles.input}
onChangeText={onChangeText}
/>
</View>
<Text style={styles.output}>{translated}</Text>
<View style={styles.button}>
<Button
title="START"
onPress={() => navigation.navigate("BACK_HOME")}
/>
</View>
</ImageBackground>
</View>
);
};
I created a button in flat list, when user click an specific item, it's button should change state and increment button should appear, but button changing state for all the items. I pass id too but it's not working, can someone please help me... below is my code
Items.js
<FlatList
data={this.props.items}
extraData={this.props}
keyExtractor={(items) => items.id.toString()}
numColumns={2}
renderItem={({ item }) => (
<CardBuyItem>
<Image style={styles.image} source={item.image} />
<View style={styles.detailContainer}>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.subTitle} numberOfLines={1}>
{item.subTitle}
</Text>
<Text style={styles.price}>Rs {item.price}</Text>
</View>
{this.props.button && this.props.added.length > 0 ? (
<View style={styles.add}>
<Text style={styles.quantity}>{item.quantity}</Text>
<MaterialCommunityIcons
style={styles.iconUp}
size={20}
name="plus-circle-outline"
onPress={() => this.props.addQuantity(item.id)}
/>
<MaterialCommunityIcons
style={styles.iconDown}
size={20}
name="minus-circle-outline"
onPress={() => this.props.subtractQuantity(item.id)}
/>
</View>
) : (
<View style={styles.buy}>
<Text
style={styles.buyonce}
onPress={() => {
this.props.addToCart(item.id);
this.props.showCart();
this.props.showButton(item.id);
}}
>
Buy Once
</Text>
</View>
)}
</CardBuyItem>
)}
/>
const mapStateToProps = (state) => {
return {
items: state.clothes.jeans,
button: state.clothes.showButton,
added: state.clothes.addedItems,
};
};
const mapDispatchToProps = (dispatch) => {
return {
addToCart: (id) => dispatch(addToCart(id)),
addQuantity: (id) => dispatch(addQuantity(id)),
subtractQuantity: (id) => dispatch(subtractQuantity(id)),
showCart: () => dispatch(showCart()),
showButton: (id) => dispatch(showButton(id)),
};
};
That's my item list with mapStateToProsp and mapDispatchToProps here button should change it's state
reducer.js
if (action.type === SHOW_BUTTON) {
let addedItem = state.jeans.find((item) => item.id === action.id);
return {
...state,
addedItem: addedItem,
showButton: action.showButton,
};
}
const initialstate = { showButton: false}
it's my reducer function with initial state of that button
action.js
export const showButton = (id) => {
return {
type: SHOW_BUTTON,
showButton: true,
id,
};
};
it's my action where I'm describing action for my reducer
You are having a common state variable for this which causes it to show all buttons.
You can do a simple solution like this.
In your flatlist you can have a logic to display the button
{this.props.added.find(x=>x.id==item.id) !=null ? (
Or if you have to use the reducer, you will have to have a property in the array and update it which would be complex to maintain.
I have a component that looks like this:
const criteriaList = [
'Nur Frauen',
'Freunde Zweiten Grades',
];
export const FilterCriteriaList: React.FunctionComponent = () => {
const [state, setState] = useState(false);
useEffect(() => {
console.log('state is,', state);
});
const myFunction = () => {
console.log('checking state', state);
if (state == false) {
setState(true);
} else {
setState(false);
}
};
return (
<View style={styles.container}>
<View style={styles.horizontalLine} />
{criteriaList.map((item: string, index: number) => (
<View key={index}>
<View style={styles.criteriaRow}>
<TouchableOpacity
onPress={() => {
myFunction();
}}>
<Icon
name="circle-thin"
color="#31C283"
size={moderateScale(20)}
/>
</TouchableOpacity>
<Text style={styles.text}>{item}</Text>
</View>
<View style={styles.horizontalLine} />
</View>
))}
</View>
);
};
Currently, I am using the circle-thin icon. I want to change it such that everytime I click on an icon, it changes to the dot-circle-o icon. Like radio buttons. However, I am not quite sure how to do so.
I thought of using ternary operators but since I am mapping my fields Idk how to setStates collectively. Maybe using the index? I don't want to make a separate state for each field. Here's a similar snack demo:
https://snack.expo.io/toTSYc2fD
I want to be able to select multiple/unselect options. I don't want to apply the same rule on all fields together.
Note: the onPress function can also be used on the Icon directly instead of the TouchableOpacity (though it is not preferred)
Using a ternary sounds like the right approach to me. Can you not do something like:
name={state ? 'dot-circle-o' : 'circle-thin'}
You could also refactor your function:
const myFunction = () => {
console.log('checking state', state);
setState(!state)
};
If you have multiple fields then there are many ways to handle it. You could call useState multiple times, eg:
const [field1, setField1] = useState(false);
const [field2, setField2] = useState(false);
You could also store all fields in the same state:
const [state, setState] = useState({field1: false, field2: false});
...
const myFunction = (fieldName) => {
console.log('checking state', state);
setState({...state, [fieldName]: !state[fieldName]})
};
I guess you'd then use the item as the "fieldName? In which case:
return (
<View style={styles.container}>
<View style={styles.horizontalLine} />
{criteriaList.map((item: string, index: number) => (
<View key={index}>
<View style={styles.criteriaRow}>
<TouchableOpacity
onPress={() => {
myFunction(item);
}}>
<Icon
name={state[item] ? 'dot-circle-o' : 'circle-thin'}
color="#31C283"
size={moderateScale(20)}
/>
</TouchableOpacity>
<Text style={styles.text}>{item}</Text>
</View>
<View style={styles.horizontalLine} />
</View>
))}
</View>
);
And to create the initial state:
const initialState = {}
criteriaList.forEach(item => initialState[item] = false)
const [state, setState] = useState(initialState);
The code would be something like below.
You have to set the index of the selected item as the state and use it to chose the icon.
const criteriaList = [
{title:'My List',checked:false},
{title:'Friends listt',checked:false},
{title:'Not Good',checked:false},
{title:'Sweet and sour',checked:false},
{title:'Automatic',checked:false},
];
export const FilterCriteriaList: React.FunctionComponent = () => {
const [state, setState] = useState(criteriaList);
useEffect(() => {
console.log('state is,', state);
});
const myFunction = (index) => {
console.log('checking state', state);
const arr=[...state];
arr[index].checked=arr[index].checked?false:true;
setState(arr);
};
return (
<View style={styles.container}>
<View style={styles.horizontalLine} />
{criteriaList.map((item: Any,index:number) => (
<View key={item}>
<View key={item} style={styles.criteriaRow}>
<Icon
style={styles.icon}
name={item.checked?"circle":"circle-thin"}
color="#31C283"
size={moderateScale(20)}
onPress= {()=>myFunction(index)}
/>
<Text style={styles.text}>{item.title}</Text>
</View>
<View style={styles.horizontalLine} />
</View>
))}
</View>
);
};