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

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 :)

Related

Updating informations from modal-React native

On my react native app I display information that I fetched from the server this way:
So when I click update profil, I display a modal with text input on it in order to give the user the opportunity to change the information of his profile. The modal look like this:
Now I already created a Fetch Post function that, when I click on the button update it sends static information to the server and the modal closes. but the profile page doesn't refresh until I get out of it and come back.
My question is: whats the best way to get the values from the textinputs, send them through post. and refresh the screen after the modal closes?. Should i use formik?
Here is a look at my code:
export default function MprofilScreen({ navigation }) {
const [modalVisible, setModalVisible] = useState(false);
const [Data, setData] = useState([]);
useEffect(() => {
fetch('******')
.then((response) => response.json())
.then((res) => {
console.log("repooooonse")
console.log(res)
setData(res)
})
.done();
}, []);
return (
<View style={{ flex: 1, backgroundColor: 'white' }} >
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => {
Alert.alert("Modal has been closed.");
}}>
<View style={styles.modalView}>
<TouchableOpacity
style={{ ...styles.openButton, backgroundColor: "#2196F3" }}
onPress={() => {
setModalVisible(!modalVisible);
}}
>
<Text style={styles.textStyle}>close</Text>
</TouchableOpacity>
<ScrollView>
<Text style={styles.text}>Nom:</Text>
<TextInput style={styles.text_input} placeholder="nom" />
....
<Text style={styles.text}>Ville :</Text>
<TextInput style={styles.text_input} placeholder="Ville " />
<TouchableOpacity
style={{ ...styles.openButton, backgroundColor: "#2196F3" }}
onPress={() => {
setModalVisible(!modalVisible);
}}
>
<Text style={styles.textStyle}>Delete</Text>
</TouchableOpacity>
</ScrollView>
</View>
</Modal>
<TouchableOpacity style={styles.btn}
onPress={() => {
setModalVisible(true);
}}>
<Text style={{ color: 'white', fontSize: 15 }}> Update profil</Text>
</TouchableOpacity>
<View >
<View style={styles.main_container}>
<Text style={styles.text}>Nom:</Text>
<Text style={styles.text1}>{Data.nom}</Text>
</View>
.....
<View style={styles.main_container}>
<Text style={styles.text}>Ville:</Text>
<Text style={styles.text1}> {Data.ville}</Text>
</View>
</View>
</View>
);
}
I'm new to react native and I'll appreciate your help!
I'll assume you have the fetching details in componentDidMount of that profile page, and since modal also resides in it, so page doesnt refresh. What you can do is call that function again on modalClose.
suppose you have,
getDetails = () => {
.... fetch details
}
and in componentDidMount you call like :
componentDidmount(){
this.getDetails();
}
So same you can call on modalClose the same function after updating it.
onModalClose = () => {
this.getDetails()
}
hope its clear.feel free for doubtys

Render via map instead of for loop

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],
);

How to avoid jsx styling error in react native?

Whenever I tried to build UI from scratch I'm getting this error adjacent jsx element must be wrapped in an enclosing tag. I don't know how to solve this. Because I tried different methods, I've tried to put the blocks within View component withflex:1 but non-use. Is there any proper solution for this. This is becoming a great challenge for me because I can't design any components of my own. What to do please help me. Following is my code.
screen.js
export default class FirstScreen extends Component {
constructor(props){
super(props);
this.state = {
showPopupDialog: false,
workType: "",
workers: []
}
}
componentWillMount(){
fetch('http://192.168.1.6:3000/api/worker', {
method:'GET',
headers:{
Accept: 'application/json'
}
})
.then(response => response.json())
.then(responseData =>
this.setState({
workers:responseData
})
)
}
onPressYes = (workType) => {
console.log(workType);
}
popupDialog = (id, workType) => {
this.setState ({
showPopupDialog: true,
workType: workType
});
//make sure you set showPopupDialog to false and workType to "" when you click yes or no button in PopupDialog component so that it will work the next time you click on card
}
render() {
const { workers, workType, showPopupDialog} = this.state;
return (
<View style={{flex:1}}>
<Header />
<ScrollView>
{workers.map((a, index)=> (
<View style={{flex:1}}>
<CardSection>
<TouchableOpacity onPress={() => this.popupDialog(a.id, a.work_type)}>
<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>
</View>
))}
{showPopupDialog && <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(workType)}/>
</View>
<View style={styles.button_1}>
<Button title="No" color="#FF6633" onPress={() =>this._onPressNo() }/>
</View>
</View>
</View>
</PopupDialog>}
</ScrollView>
</View>
);
}
}
The issue I'm facing is I can't place the <PopupDialog> component adjacent to <CardSection> , in order to that I put the <PopupDialog> within <View> ,even though it doesn't solve my issue.Please help..Please
Give a try with below corrected code.
There are two things that needs be corrected
You are doing .map but you are not returning anything which I have
corrected in the code below
export default class FirstScreen extends Component {
constructor(props){
super(props);
this.state = {
workType: "",
workers: []
}
}
componentWillMount(){
fetch('http://192.168.1.6:3000/api/worker', {
method:'GET',
headers:{
Accept: 'application/json'
}
})
.then(response => response.json())
.then(responseData =>
this.setState({
workers:responseData
})
)
}
onPressYes = (workType) => {
console.log(workType);
}
popUpDialog = (id, workType) => {
this.setState ({
workType: workType
});
this.popupDialog.show();
//make sure you set workType to "" when you click yes or no button in PopupDialog component so that it will work the next time you click on card
}
render() {
const { workers, workType} = this.state;
return (
<View style={{flex:1}}>
<Header />
<ScrollView>
{workers.map((a, index)=> (
<View style={{flex:1}}>
<CardSection>
<TouchableOpacity onPress={() => this.popUpDialog(a.id, a.work_type)}>
<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>
</View>
))}
<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(workType)}/>
</View>
<View style={styles.button_1}>
<Button title="No" color="#FF6633" onPress={() =>this._onPressNo() }/>
</View>
</View>
</View>
</PopupDialog>
</ScrollView>
</View>
);
}
}
If I understand your question correctly...
You can return multiple root elements in jsx by wrapping is in a <React.Fragment> element (you can use <> and </> in v16.2 and later). Fragments are new in React v16. Prior to that, you just have to wrap them in some element (a div or span, usually).
The problem is that you have this structure:
<a>
{this.state.workers.map((a, index)=>
<b/>
<c/>
)}
</a>
Since <b/><c/> is parsed separately and there's no enclosing element, you're getting the error. But an enclosing element isn't necessary for the final structure, which does have an enclosing element. The solution is to simply return an array of JSX elements, like this:
<a>
{this.state.workers.map((a, index)=>
[<b/>,
<c/>]
)}
</a>

Method's return doesn't display [React Native]

I have an issue with a method in my code (React Native).
I have an array of IDs and I would like to make a 'GET' request in each ID to display a preview card for each.
That's why I used the 'map' method on my array.
The request works fine and then I call my method cardUser(user) to display.
That's the part of my code where I do the map (Works fine):
render() {
if (!this.state.isLoaded) {
return (
<Spinner color='#84568c' />
);
} else {
var user = this.state.user;
return (
<ScrollView
refreshControl={
<RefreshControl
tintColor='#84568c'
refreshing={this.state.refreshing}
onRefresh={this._onRefresh.bind(this)} />
}
>
<Content padder>
{user.following.map(following => (
this.getUserByID(following)
))}
</Content>
</ScrollView>
);
}
}
I call getUserByID() (Works fine):
getUserByID(id) {
fetch(api.base_url + api.user_url + '/' + id, {
method: "GET",
headers: { 'Authorization': 'Bearer ' + this.state.token }
})
.then((response) => response.json())
.then((responseData) => {
this.cardUser(responseData);
})
.done();
}
Now that I have my response in responseData I want to diplay and lay out each user. So I call cardUser() with an user object each time.
cardUser(user) {
console.log(user.id);
return (
<TouchableHighlight underlayColor='transparent' key={user.id}
onPress={() => this.props.navigation.navigate('UserProfile', {id: user.id})} onLongPress={() => this.alertUserId(user.id)} >
<View style={container.card}>
<View style={container.cardUser}>
<Image source={{uri: user.banner}} style={{position: 'absolute', width: '100%', height: 170, borderRadius: 20}} />
<View style={container.darkLayer}>
<Left>
<Thumbnail large source={{uri: user.photo}} style={{alignSelf: 'center'}}/>
</Left>
<Body>
<View style={{alignItems: 'center'}}>
<Text style={text.pseudoHomepage}>{user.username}</Text>
<Text style={[text.stats, {color: 'white'}]}>{user.first_name}</Text>
</View>
</Body>
<Right>
<Icon ios='ios-medal' android='md-medal' style={{color: 'white', alignSelf: 'center', fontSize: 40}}/>
</Right>
</View>
<View style={container.darkFooterLayer}>
<Text numberOfLines={2} style={text.bioFotter}>{user.bio}</Text>
</View>
</View>
</View>
</TouchableHighlight>
);
}
But that's not display anything... However if I make a console.log(user) it shows the right things in my logs ! In the right order and no duplicated object or anything else...
Moreover if I put a console.log(user) in the return() it shows the right thing too and I don't have any error or warning message.
I hope I was clear enough, if you need additional informations tell me.
Have a nice day thank you !
(Sorry if I misspelt, English is not my language)
First of all, you need to fetch all the data. You can't do it in the render method.
componentDidMount() {
Promise.all(this.state.user.following.map(this.fetchUser))
.then(userFetchedArray => this.setState({ usersList: userFetchedArray }))
}
fetchUser(id) {
return fetch(api.base_url + api.user_url + '/' + id, {
method: "GET",
headers: { 'Authorization': 'Bearer ' + this.state.token }
})
.then((response) => response.json());
}
Then you need to render fetched data:
render() {
...
<Content padder>
{this.state.usersList.map(this.cardUser)}
</Content>
...
Callback passed to map should return jsx, but in your case, it returns undefined (getUserByID function return nothing). Also this callback can't be an async

how to add pull to refresh with redux react native

I wanna add pull to refresh but i dont know what to call from from _Refresh(). I have action, constants and reducers in another page. How can i recall the api.
thanks for help in advance.
I wanna add pull to refresh but i dont know what to call from from _Refresh(). I have action, constants and reducers in another page. How can i recall the api.
thanks for help in advance.
class HomeworkList extends Component {
constructor(props){
super(props);
this.state = {
getHW : null,
refreshing: false,
appState: AppState.currentState,
months : ["Jan","Fev","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]
}
}
_onRefresh() {
this.setState({refreshing: true});
}
componentDidMount(){
this.props.getHomework();
}
render() {
const {homework,isFetching} = this.props.homework;
if(isFetching){
return(
<View>
<ActivityIndicator
color = '#bc2b78'
size = "large"
/>
</View>
)
}
else{
return (
<ScrollView style={styles.container} refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this._onRefresh.bind(this)}
/>
}>
<View style={styles.filterView}>
<View style={{flexDirection:'row'}}>
<Icon size={20} name="ios-options" color="#000000" /><Text style={[{color:"#333333"},mainStyles.postTitle]}> FILTER BY</Text>
</View>
<View>
<ScrollView
horizontal={true}
showsHorizontalScrollIndicator={false}
>
{
this.state.months.map((item,i)=>{
return(
<TouchableHighlight key={i} style={styles.filterItem} onPress={() => {}} underlayColor={"#de0000"}><Text >{item}</Text></TouchableHighlight >
)
})
}
</ScrollView>
</View>
</View>
<View style={[styles.titleView,mainStyles.coloredBackground]}>
<TouchableOpacity
>
<Text style={styles.title}>
THIS MONTH
</Text>
</TouchableOpacity>
</View>
<View style={styles.viewPadding}>
{
homework.length ? (
homework.map((item, index) => {
return(
<TouchableOpacity
onPress={() => this.props.navigate('Chat', { title: item })}
key={item.id}
>
<Text style={[styles.listItems,{borderColor:randomHex()}]}>
{item.date}
</Text>
</TouchableOpacity>
)
})
):null
}
</View>
</ScrollView>
);
}
}
}
function mapDispatchToProps(dispatch){
return{
getHomework: () => dispatch(fetchHomeworkFromApi()),
getNaviagationName:()=>dispatch(getNaviagationName())
}
}
function mapStateToProps(state){
return{
homework: state.homework
}
}
export default connect(mapStateToProps,mapDispatchToProps)(HomeworkList);
Dispatch a refresh action inside the refresh callback.
This action should then dispatch any number of other actions to "re fresh" like fetchHomeworkFromApi and getFreshData. Don't re-fetch things that make no sense to refresh.
Inside your reducers make sure that when fresh data arrives, it replaces the old. (Avoid situations where new data is appended to old data)
Very simple, assuming you have API call defined in your redux action, hooked up with redux thunk, something like below
// Action.js
const reduxAction = () => {
return async dispatch => {
dispatch({ type: IS_LOADING });
//Call backend API
const result = axios.xxx();
dispatch({ type: GOT_RESULT, payload: result });
}
}
The above is the very standard API calling from redux, nothing to do with pull to refresh yet.
Now since we have a redux Action for our component, and that you have RefreshControl, you need to update your `_
_onRefresh() {
this.props.reduxAction();
}
As for your RefreshControl, instead of using localState, use the loading state from your redux, something like below
<RefreshControl
refreshing={this.props.refreshing} //Use the one from redux, not component state
onRefresh={this._onRefresh.bind(this)}
/>

Categories

Resources