Render via map instead of for loop - javascript

I am currently using this function to render some elements & display results after a graphql query:
const showUsers = React.useCallback(
(data: UsersLazyQueryHookResult, numberOfUsers: Number) => {
if (data) {
for (var i = 0; i < numberOfUsers; i++) {
const userName = data.users.nodes[i].firstName
.concat(' ')
.concat(data.users.nodes[i].lastName);
return (
<View style={styles.friends}>
<View style={styles.item}>
<Text style={styles.userName}>{userName}</Text>
<View style={styles.addButtonContainer}>
<Button
rounded
onPress={() => {
addFriend(Number(data.users.nodes[i].id));
setIsSubmitted(false);
setUserData(null);
}}>
<Icon name="plus" size={moderateScale(20)} color="black" />
</Button>
</View>
</View>
</View>
);
}
}
},
[createUserRelationMutation, userData, numberOfUsers],
);
I have read that using a for loop isn't a good idea. Hence, I am trying to switch to a map but I am unable to. I couldn't figure out how to use variables like const userNamewhile using a map.
Currently, I can only test with numberOfUsers = 1so it works fine but in reality, I want all of the item contained in the Viewwhich is styled as friends. For now, there will be a separate <View style={styles.friends}>for each item. However, I want to map all items inside one single <View style={styles.friends}>

Map takes a function as its argument, which means that you can use that same code from the for loop inside of the function passed to the map, like this:
data.users.map((user) => {
const userName = user.firstName
.concat(' ')
.concat(user.lastName);
return (
<View style={styles.friends}>
<View style={styles.item}>
<Text style={styles.userName}>{userName}</Text>
<View style={styles.addButtonContainer}>
<Button
rounded
onPress={() => {
addFriend(Number(user.id));
setIsSubmitted(false);
setUserData(null);
}}>
<Icon name="plus" size={moderateScale(20)} color="black" />
</Button>
</View>
</View>
</View>
);
}
Just replace all instances of data.users.nodes[i] with user since that's what each object in the array is passed into the function as.
For more info about this, check this part of the React docs.

If you want everything to be contained inside the view styled as friends this is how the code should be.
You should have the map inside the view as JS code and access properties from the item variable.
const showUsers = React.useCallback(
(data: UsersLazyQueryHookResult, numberOfUsers: Number) => {
if (data) {
return (
<View style={styles.friends}>
{
data.users.nodes.map(item => {
const userName = item.firstName
.concat(' ')
.concat(item.lastName);
return (<View style={styles.item}>
<Text style={styles.userName}>{userName}</Text>
<View style={styles.addButtonContainer}>
<Button
rounded
onPress={() => {
addFriend(Number(item.id));
setIsSubmitted(false);
setUserData(null);
}}>
<Icon name="plus" size={moderateScale(20)} color="black" />
</Button>
</View>
</View>);
})
}
</View>
);
}
},
[createUserRelationMutation, userData, numberOfUsers],
);

Related

undefined is not a function(near '...datas.map....')

This is my data
[{"size":"Small","price":"90"},{"size":"Large","price":"180"},{"size":"Extra Large","price":"200"}]
and this is my code
const route = useRoute();
const [datas, setDatas] = useState([]);
useEffect(() => {
setDatas(route.params.item_sizes);
},[])
const ItemSizes = () => {
if(datas.length > 0)
{
console.log("Item size data: ", datas); //for debugging purposes
return(
<View>
{datas.map((data,key) => (
<View key={key}>
<View style={{flex:0.2}}>
<MaterialCommunityIcons name={"radiobox-blank"} size={20} color={'gray'}/>
{/* <Text style={{fontSize:16, fontWeight:'bold'}}>Icon</Text> */}
</View>
<View style={{flex:1}}>
<Text style={{fontSize:16, fontWeight:'bold'}}>{data.size}</Text>
</View>
<View style={{flex:1, flexDirection:'row-reverse'}}>
<Text style={{fontSize:16, fontWeight:'bold'}}>{data.price}</Text>
</View>
</View>
))}
</View>
)
}
}
I call it from my view like this
return (
<View>
{ItemSizes()}
</View>
)
So after I checked there's a data on datas so why it is undefined? Please do explain why it is returning me undefined even though there's a data?? Thank you
Check if the data you are setting is an array or string. If it's string set it like below by parsing
setData(JSON.parse(route.params.item_sizes));

How to pass up nested state and avoid useCallback in react native

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 get my api call from google-translate to work

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>
);
};

Why external function can't navigate other javascript file in React?

I'm making simple app which contains Todo App.
However, the function which locate in outside of main function(Is this expression right?) in same Javascript file can't navigate to other screen.
Below is my Screen structure(simplified)
<Stack.Screen name="Myqtwrite" component={MyqtwriteScreen} options={{headerShown: false}} />
<Stack.Screen name="Myqtupdate" component={MyqtupdateScreen} options={{headerShown: false}} />
And the problem is below.
I expressed the location of problem.
MyqtwriteScreen.js
export function Note(props, {navigation, route}) {
return (
<View style={[styles.myblock, {backgroundColor:props.val.color}]}>
<View style={{alignItems:'flex-start'}}>
<View style={{borderBottomWidth:2,borderColor:'white',marginBottom:5}}>
<Text style={[styles.myblocktitle]}>{props.val.date}</Text>
</View>
<Text style={styles.myblocktext}>{props.val.note}</Text>
</View>
<View style={{width: '100%' ,flexDirection:'row',justifyContent:'flex-end'}}>
/* problem is here--> */ <TouchableOpacity onPress={() => {navigation.navigate('Myqtupdate') }} style={{marginTop:5,marginLeft:10}} >
<Text style={styles.myblockbuttontext}>UPDATE</Text>
</TouchableOpacity>
<TouchableOpacity onPress={props.deleteMethod} style={{marginTop:5,marginLeft:10}} >
<Text style={styles.myblockbuttontext}>DELETE</Text>
</TouchableOpacity>
</View>
</View>
);
}
export function MyqtwriteScreen({ navigation, route }) {
const [noteArray, setNoteArray] = useState([]);
const [noteText, setNoteText] = useState('');
let rdcolor = 'hsl('+Math.random()*255+','+ Math.random()*(60)+10+'%, 82%)';
let notes = noteArray.map((val, key) => {
console.log('start');
return <Note key={key} keyval={key} val={val}
deleteMethod={() => deleteNote(key)} />
});
const addNote = () => {
if (noteText) {
var d = new Date();
noteArray.unshift({
'date': d.getFullYear() +
"Y " + (d.getMonth() + 1) +
"월 " + d.getDate() + "일 " + d.getHours() + "시 " + d.getMinutes()+"분",
'note': noteText,
'color': rdcolor,
});
setNoteArray(noteArray);
setNoteText('');
// alert('큐티 입력을 완료했습니다.');
}
else {
alert('큐티를 입력하세요');
}
};
const deleteNote = (key) => {
const newArray = [...noteArray];
newArray.splice(key, 1);
setNoteArray(newArray);
};
return (
<View style={styles.container}>
<View style={styles.topbar}>
<Text style={styles.topbartext}>오늘의 큐티</Text>
<Icon style={styles.topbarmenu} name="close" onPress={() => { navigation.navigate('My') }} />
</View>
<View style={styles.qtinputblock}>
<TextInput
onChangeText={(noteText) => setNoteText(noteText)}
value={noteText}
placeholder='큐티를 입력하세요'
placeholderTextColor='gray'
multiline={true}
style={styles.qtinputtext}
/>
<TouchableOpacity onPress={addNote} style={styles.myblockbutton}>
<Text style={styles.myblockbuttontext}>추가</Text>
</TouchableOpacity>
</View>
<ScrollView style={styles.scroll}>
<View style={{alignItems:'flex-start'}}>
{notes}
</View>
</ScrollView>
</View>
);
}
I made 'MyqtupdateScreen.js' file of course.
The most interesting thing is that when I wrote same onPress={() => {navigation.navigate('Myqtupdate') }} in some TouchableOpacity in the 'function MyqtwriteScreen({navigation, route'})' , it works!!
I don't understand why navigation.navigate('Myqtupdate') doesn't work in export function Note, but works in export function MyqtwriteScreen.
Any words would be help!
Note doesnt get a navigation prop you will have to pass it like below
return <Note key={key} keyval={key} val={val} navigation={navigation} deleteMethod={() => deleteNote(key)} />
It works on the other screen as its part of the stack.
You can also use the useNavigation hook to get access to navigation.

How to pass arguments within onclick of button in react native?

I am trying to pass a data (server response) as an argument of a button.Actually in my case there are certain type of workers (listing within cards ). If clicked on a worker it should be saved to db with the corresponding worker's id.Upon clicking on the card of workers there will be a popup showing for confirmation.So if clicked on yes button I'm taking the corresponding worker's id and perform another fetch request for saving it to my db.But this is not working I'm confused how to pass arguments within onclick property of a button and take that argument within fetch method.Following is my code.I'm pasting only a portion of my code below.
updated code
export default class FirstScreen extends Component {
constructor(){
super();
this.state = {
workers: [],
}
}
componentWillMount(){
fetch('http://192.168.1.3:3000/api/worker', {
method:'GET',
headers:{
Accept: 'application/json'
}
})
.then(response => response.json())
.then(responseData =>
this.setState({
workers:responseData
})
)
}
onPressYes = (worker_id) => {
fetch('http://192.168.1.3:3000/api/work_detail',{
method:'POST',
headers:{
Accept:'application/json'
},
body:JSON.stringify({
worker_id
})
})
}
render() {
return (
<View style={{flex:1}}>
<Header />
<ScrollView>
{this.state.workers.map((a, index)=>
<Container>
<CardSection>
<TouchableOpacity
onPress={() => this.popupDialog.show()}
>
<View style={{ maringTop: 10, marginLeft:120}}>
<Image
style={{ height: 100, width: 100 }}
source={{ uri: a.work_type == 'Carpenter' ? images[0].image : images[1].image}}
/>
<Text style={{marginLeft:20, fontSize:20}}>{a.work_type}</Text>
</View>
</TouchableOpacity>
</CardSection>
</Container>
<View style={styles.buttonContainer}>
<TouchableOpacity onPress={() =>console.log('Clicked')}>
<Button
backgroundColor="#FF4500"
title='View Status' />
</TouchableOpacity>
</View>
</ScrollView>
<PopupDialog
ref={popupDialog => {
this.popupDialog = popupDialog;
}}
dialogStyle={{ backgroundColor: "#FFFFFF", height: 180, width:300, borderWidth:1,padding:10}}
overlayBackgroundColor="#fff"
dismissOnTouchOutside={true}
>
<View style={styles.dialogContentView}>
<Text style={{fontSize:18, margingTop:10,color:"#000000"}}>Are you sure you want to submit?</Text>
<View style={{flexDirection:'row'}}>
<View style={styles.button_1}>
<Button
title="Yes"
color="#FF6633"
onPress={() => this.onPressYes(worker_id)}
/>
</View>
<View style={styles.button_1}>
<Button
title="No"
color="#FF6633"
onPress={() =>this._onPressNo() }
/>
</View>
</View>
</View>
</PopupDialog>
</View>
})
);
}
}
workers is the array I'm fetching from server.
Can you try replacing _onPressYes = (a.worker_id) with with _onPressYes = (worker_id) and then
body:JSON.stringify({
worker_id
})
Let me know if that helps.
What I usually do is return a function with the given parameter. I mean wrap a function in a function like the following:
onClickHandler = (value) => () => {
// do whathever you want.
};
<Component onClick={this.onClickHandler(yourValue)}/>
Because the problem in your code is that the function will be execute without calling the onClick event because when you pass the argument to a simple function you are already calling it.
I hope it can help you :)

Categories

Resources