Icon changes if value changes - react-native

i have a like icon and value of status 0 or 1.want to show thumbs up icon if status is 1 and thumbs down if status is 0.and toggle the same when clicked
<View style={{marginTop:10}}><Icon name={itemData.wliststatus===0?"thumbs-up":"thumbs-down"} size={16} type="font-awesome" onPress={() => this.navigateToApp(itemData)}/></View>
Fetched from API
itemdata:{
"userid": "1",
"views": "258",
"wliststatus": 0,
}
<View style={{marginTop:10}}><Icon name={itemData.wliststatus===0?"thumbs-up":"thumbs-down"} size={16} type="font-awesome" onPress={() => this.navigateToApp(itemData)}/></View>

First create state variable that holds whliststatus value and create function that toggle value of state variable.
state = { whliststatus = false }
const handlePress = itemData => this.setState({ whlistatus: !this.state.whlistatus }, () => { this.navigateToApp(itemData)} })
<View style={{marginTop:10}}><Icon name={this.state.whliststatus ? "thumbs-up" : "thumbs-down"} size={16} type="font-awesome" onPress={this.handlePress(itemData)}/></View>

Use state to change items dynamically.
First, in state variable, set your like status value as false.
Remember: the state variable will reside globally in your class
state = {
liked: false
};
Then in componentDidMount, after successfully fetching data, update your status accordingly in state:
componentDidMount() {
fetch(...)
.then(res => res.json())
.then(res => {
...
itemdata:{
"userid": "1",
"views": "258",
"wliststatus": 0,
}
// here update "liked" status in state
this.setState({ liked: itemdata.wliststatus == 0 ? true : false });
...
})
.catch(error => console.log(error));
}
Now, come directly to the render method, get or set the state and data will be handled automatically:
render() {
const { liked } = this.state;
return (
<View style={{marginTop:10}}>
<Icon name={ liked ? "thumbs-up" : "thumbs-down" }
size={16} type="font-awesome"
onPress={() => this.navigateToApp()}/>
</View>
);
}
Then as it seems you might be updating the like in this.navigateToApp function. There in this method you can update your state as:
navigateToApp() {
const { liked } = this.state;
...
// here just switch your liked status
this.setState({ liked: !liked });
...
}

Related

API call function in useEffect creates infinite loop in React Native

I have a Component where i'm maping an Array to render data. But before that, i need to call API taking id from each objects of the array to modify the array. Now, i am calling the API's in a function and calling the function in useEffect() . But when i do that, it continues to an infinite loop. Here's how my component looks like:
const DemoComponent = (props) => {
const [renderArr, setRenderArr] = useState([]);
useEffect(() => {
getStatus();
},[renderArr])
const getStatus= async() =>{
var arr = [
{id: 1,name: Leather},
{id: 2,name: Shoe},
{id: 3,name: Belt},
]
var firstStatus = await API(arr[0].id , props.token)
var secondStatus = await API(arr[1].id , props.token)
var thirdStatus = await API(arr[2].id , props.token)
var statusObj = [
{ status: firstStatus.status },
{ status: secondStatus.status },
{ status: thirdStatus.status },
]
var mergedArray = newArr.map((e, i) => ({ ...e, ...statusObj[i] }));
setRenderArr(mergedArray);
}
}
return (
<View style={styles.container}>
{mergedArray.map((item, index) => {
return (
<TouchableOpacity>
<Text style={{ color: '#FFF' }}>{item.status}</Text>
</TouchableOpacity>
);
})}
</View>
);
};
Now, how can i stop this infinite loop. But in the meantime, i want to rerender when renderArr changes props.
Because you trigger the renderArr. You should do one call only when the component is mounted such as below snippet.
useEffect(() => {
getStatus();
}, []);
There are a few items I would like to point out here,
You have side effect registered for renderArr, which calls a function that updates renderArr. Any change in renderArr will invoke the side effect and this loop goes on forever.
Inside the getStatus function, you are updating your renderArr state after your application logic. But the render part is referring to some other variable mergedArray. You will have to change it to renderArr state.
const DemoComponent = (props) => {
const [renderArr, setRenderArr] = useState([]);
useEffect(() => {
getStatus();
},[]); // removed dependency to call it only once, (on mount)
const getStatus= async() =>{
....
setRenderArr(mergedArray); // state is updated with the new processed value
}
return (
<View style={styles.container}>
{renderArr.map((item, index) => { // changed to the state variable
return (
<TouchableOpacity>
<Text style={{ color: '#FFF' }}>{item.status}</Text>
</TouchableOpacity>
);
})}
</View>
);
};

Why response type is different?

I am building a simple gallery management app with React Native.
In gallery page, I called "FetchGallery" function and in there, I got some response.
This is constructor part.
this.state = {
mainGalleryData: [
{
bg_url: '',
country_id: 0
}
],
};
That is the code for response.
_onFetchGalleryBySite = (e) => {
fetch(config.api.getGalleryInfo + '/' + e, {
method: 'GET',
headers: this.state.myHeaders
})
.then((response) => response.json())
.then((responseJSON) => {
console.log('resJSON=>', responseJSON['gallery_list']); // => log is in the below.
responseJSON['gallery_list'].map(item => {
if (item != "") {
let obj = {}
obj.bg_url = item.bg_url
obj.country_id = item.country_id
this.state.mainGalleryData.push(obj)
}
})
})
.catch(err => console.log('_onFetchGalleryInfoErr=>', err))
}
This is the log of fetched data
resJSON => [{ "bg_url": "staff_upload/bgremoval_20201008030228.png", "country_id": "3" },
{ "bg_url":"Guest/1/image/bgremoval_20201004222851.png", "country_id": "3" }]
And in the render() , I used this data in the flatList
That is the code for FlatList
<FlatList
data={mainGalleryData}
renderItem={this.renderGallery}
keyExtractor={(item, index) => index.toString()}
/>
This is the renderGallery.
renderGallery = (item) => (
<TouchableOpacity onPress={() => console.log('itemClicked=>', item)} style={styles.overlay}>
<Image source={newImg} style={styles.newImg} />
{
this.state.gallery_id == item.location_id ?
<Text style={{ fontWeight: '700' }} numberOfLines={1}>{item.item.title}</Text>
:
<Text style={{ fontSize: 12 }} numberOfLines={1}>{item.item.title}</Text>
}
</TouchableOpacity>
)
When I click one of those data, I want to get this data. But the result is different that I expect.
I want: { "bg_url": "staff_upload/bgremoval_20201008030228.png", "country_id": "3" }
result: { "index": 2, "item": {"Guest/1/image/bgremoval_20201004222851.png", "country_id": "3"}}
So I had to get country_id with "item.item.country_id".
I want to get country_id with "item.country_id".
What is this <"index": 2> ?
Before pushing response in state, please clear "mainGalleryData[]"
because default it has 1 item in state which makes index
inappropriate.
Do this
console.log('resJSON=>', responseJSON['gallery_list']);
const responseData = responseJSON['gallery_list'];
const data = this.state.mainGalleryData.concat(responseData); <-- add this line -->
this.state({ mainGalleryData: data });

Why is my entered text in react-native SearchBar is getting cleared automatically after some milliseconds?

My entered text in SearchBar is getting cleared automatically after some milliseconds when I start typing into it. It goes back to the placeholder state, what could be the issue?
Here is my code:
return (
<View style={{ marginTop: 30 }}>
<SearchBar
placeholder="Type Here..."
lightTheme
round
onChangeText={this.handleSearch}
/>
</View>
);
};
this is the code for handleSearch method,
handleSearch = (text) => {
const formatQuery = text.toLowerCase();
const data = _.filter(this.state.fullData, (user) => {
return contains(user, formatQuery);
});
this.setState({ query: formatQuery, data }, () => this.makeRemoteRequest());
};
code for makeRemoteRequest():
makeRemoteRequest = _.debounce(() => {
this.setState({ loading: true });
getUsers(20, this.state.query)
.then((users) => {
this.setState({
loading: false,
data: users,
fullData: users,
});
})
.catch((error) => {
this.setState({ error, loading: false });
});
}, 250);
The issue persists even if I remove the debounce method so I think the issue is related to something else.
Also, this is like my 3rd day with react-native development so please excuse any newbie mistakes.
if i am not mistaken, you should add a value prop to the SearchBar, and put a state variable like so :
<SearchBar
placeholder="Type Here..."
lightTheme
round
onChangeText={this.handleSearch}
value={this.state.text} //use deconstructing
/>
And update it before doing your handle search
handleSearch = (text) => {
this.setState({text})
const formatQuery = text.toLowerCase();
const data = _.filter(this.state.fullData, (user) => {
return contains(user, formatQuery);
});
this.setState({ query: formatQuery, data }, () => this.makeRemoteRequest());
};
You should check the SearchBar doc from react-native-elements : https://react-native-elements.github.io/react-native-elements/docs/searchbar.html

How to change checkbox status in React Native

Here I want to change checkbox status. After clicking to checkbox no change does not appear. But after CTR+S in VS Code the data is updated.
const [data,setData] = React.useState();
React.useEffect(() => {
DB.select("tasks","*").then(res=>{
return setData(res);
});
}, [100])
const complete = (index,id)=>{
data.forEach((x,k)=>{
if(k == index){
x.status = !x.status;
alert(x.name);
DB.update("tasks",["status"],[x.status],'id=?',[x.id]).then(r=>{
console.log(r);
});
setData(data);
}
});
}
<FlatList
style={css.list}
data={data}
extraData={data}
renderItem={({ item,index }) =>
<View style={css.item}>
<View style={css.textItem}>
<Text>{item.name}</Text>
<Checkbox
status={item.status ? 'checked': 'unchecked'}
onPress={e=>complete(index,id) }
/>
</View>
<View style={css.chip}><Text style={{alignItems: 'center',color: "#009386"}}>Category #1</Text></View>
</View>}
keyExtractor={data => data.id}
/>
You're setting the same data which was on the state. So it won't update.
In addition
It is very bad practice to direct;y mutate the state variable what you're doing in complete function. Use map instead of forEach.
Update your complete function as follows:
const complete = (index, id) => {
const updatedData = data.map((x, k) => {
x.status = x.status ? 0 : 1;
DB.update("tasks", ["status"], [x.status], 'id=?', [x.id]).then(r => {
console.log(r);
});
return x;
});
setData(updatedData);
}
As you've told that status contains number 0/1, I'm updating status on check with 0 and 1 unlike you're saving boolean.
I code in TS, so I'm always aware of types :)

Unhandled Promise Rejection : Existing value for key "Favorites" must be of type null or array, revived string

my problem is quite simple but I'm new to react native dev. I'd like to save multiple elements with AsyncStorage (I'm using react-native-simple-store
a library that works like a wrapper but it's same logic) I want display all items for a key in a list , my code look like this:
constructor(props) {
super(props)
this.state = {
UserInput: "",
}
}
SaveValue = () => {
store.push('Favorites', this.state.UserInput)
Keyboard.dismiss()
};
FetchValue = () => {
store.get('Favorites').then((value) => {
this.setState({
favs: value
});
}).done();
};
Same thing with AsynStorage, it just update the item which is not my goal, I'd like to add a new one
SaveValue = () => {
AsyncStorage.setItem("Favorites", this.state.UserInput);
Keyboard.dismiss()
};
FetchValue = () => {
AsyncStorage.getItem("Favorites").then((value) => {
this.setState({
favs: value
});
}).done();
};
This part is my view where I try to display data, you can also see that I use a text input and two buttons one to save and the other to display an array of items stored
render() {
return (
<View>
<TextInput
onChangeText={(UserInput) => this.setState({UserInput})}
placeholder= "Type something"
value={this.state.UserInput} />
<TouchableOpacity
onPress={this.SaveValue}>
<Text>Save</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this.FetchValue}>
<Text>Fetch</Text>
</TouchableOpacity>
<Text>{this.state.favs}</Text>
</View>
);
}
At this point I can see only one item, I tried to figure it out and saw that I have to use another method called push but when I changed save by push it throw me an error
Unhandled Promise Rejection : Existing value for key "Favorites" must be of type null or array, revived string.
Thanks!
it will work :)
renderFavorites = () => {
AsyncStorage.getItem("Favorites").then((favs) => {
favs.map((fav) => {
return (<Text> {fav} </Text>);
});
});
}
render() {
return (
<View>
<TextInput
onChangeText={(UserInput) => this.setState({UserInput})}
placeholder= "Type something"
value={this.state.UserInput} />
<TouchableOpacity
onPress={this.SaveValue}>
<Text>Save</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this.FetchValue}>
<Text>Fetch</Text>
</TouchableOpacity>
{this.renderFavorites()}
</View>
);
}
Solution using JSON:
SaveValue = () => {
const newFavs = [...this.state.favs, this.state.UserInput];
this.setState({ favs: newFavs, UserInput: '' }, () => {
AsyncStorage.setItem("Favorites", JSON.stringify(this.state.favs));
Keyboard.dismiss()
});
};
FetchValue = () => {
AsyncStorage.getItem("Favorites").then((value) => {
this.setState({
favs: JSON.parse(value)
});
}).done();
};