React Native TextInput setState() hides keyboard - react-native

Please check on the snack link,
https://snack.expo.io/#banid/textinput
The TextInput on the filter view(shows when the button is pressed) hides keyboard when ever I call setState(). I call setState to update the value of TextInput. Bcause of this I can't type continuously on the TextInput. Is this a bug or am I doing something wrong?? Thank you

The problem is that you are creating a new (anonymous) function that renders the header of the FlatList on every update
<FlatList .... ListHeaderComponent={() => this.showHeader()} />
So a new TextInput is being created instead of updating the existing one.
Solution:
change ListHeaderComponent={() => this.showHeader()}
to ListHeaderComponent={this.showHeader} as ListHeaderComponent can be a function
https://facebook.github.io/react-native/docs/flatlist#listheadercomponent
Similar Issue:
https://github.com/react-native-training/react-native-elements/issues/559

It seems the focus is being removed from the TextInput after every setState, here's a work around you can use:
Change
this.setState({
filterSearchText: text,
data: newData,
});
To
this.setState({
filterSearchText: text,
data: newData,
}, () => {
this.searchInput.focus();
});
What this does is it brings back the focus to searchInput after a setState is called

Related

Struggling with useEffect and flatlist

I am rendering a component for every item in a flatList. Each component has a label, and when the component is rendered, I have a useEffect that fetches the updated label name for that specific label.
For some reason, it seems to only be running for the last item in the flatList. The last item is the only item with the updated name, while all other still contain the outdated information.
Assuming there is an updated name for each label, why could my useEffect only be running on the last item?
<FlatList
data={labels}
keyExtractor={keyExtractor}
renderItem={renderItem}
/>
Label.js - I would think this would run for every label component rendered. Could there be a possible issue with what I have here? Or must it be somewhere else in my code?
let name = label.name;
useEffect(() => {
updateLabel()
name = label.name
}, [label]);
return (
<>
{name}
</>
)
I see several possible issues. Some important code is missing, so I'll answer what I can.
You're not using state to hold your label name in the Label component (name = label.name), so React will never know to re-render the component when it changes. It's rare to need to use a let variable in React. To hold properties that the component needs to change, use the useState hook.
However, you shouldn't do that here, because of the next point.
It looks like you are updating the label somewhere else, and also locally (name = label.name). Don't do this, it's too easy for the two to get out of sync and cause bugs. If the name is coming from somewhere else, show it and set it from props.
I'm not sure what updateLabel() does or where it comes from (how does the function know what to update the label to?), but if you need it, it should come from props.
If label.name is a string, you can't render it in a fragment. You must render it in a Text component. <Text>{label.name}</Text>
The object that FlatList passes in to the renderItem callback does not have a property called label, you are looking for item - this is the object from the data prop.
function renderLabel({ item }) { // item, not label
return <Label label={item} onPress={() => onPressLead(item)}/>;
}
const Label = ({ label, updateLabel }) => {
// no local label variable
useEffect(() => {
updateLabel(); // what is this supposed to do?
}, []); // no dependencies, if you only want to update the label once on mount
return <Text>{label.name}</Text>; // if label.name is a string
};
// your FlatList is fine as written
Your use effect probably needs the label as a dependency.
useEffect(() => {
updateLabelName()
}, [label]);

How can I create a React Native Button that saves an array value and the Button state to the device using AsyncStorage?

I am trying to create a button that changes the text when clicked and it saves a value to an array. I then need to save the state of the button and the array to the device using AsyncStorage.
I have the first part done (please see Snack link here) but I am struggling to get the AsyncStorage to work properly. If the button had been clicked once, I would expect that the array has the "item 1" value in it and the button would say clicked. Even if the app is closed and reopened, those values should still remain until the button is clicked again.
I have not found any solutions so far. Is there anyone that has some ideas?
This is the workflow that you should follow:
create an object in state (in this.state = {} for classes, or setState() with hooks) for your button's text value
initialize the above value with the value from AsyncStorage (make sure to add some conditional that if it's empty it returns [])
also take note of how Asyncstorage is async, meaning you'll have to add 'await' when you're assigning the value
add some conditional for the text value of your button, whereas it will show a loading icon, (or nothing, doesn't matter) while AsyncStorage is retrieving the initial data
onPress on your button will change the state value AND the AsyncStorage value (or you can only update the AsyncStorage value when you're closing the page with componentWillUnMount, or useEffect(() => return(--do it here--))
If you're using functional components, it would look something like this:
const [textValues, setTextValues] = useState([])
const setInitialValues = async () => {
const info = await AsyncStorage.getItem('myValues')
setTextValues(info)
}
useEffect(() => {
setInitialValues()
return(
AsyncStorage.setItem('myValues', textValues)
)
}, [])
return(
<View>
<Button onPress={() => setTextValues(textValues + 1) title={textValues.length === 0 ? '' : textValues[textValues.length - 1]}}
</View>
)

Why does Switch in ReactNative show toggle animation although its value prop is not changed? (iOS)

I am experiencing some behavior of a ReactNative Switch that I cannot explain.
I have a very simple switch (see code below). It has a fixed value prop and its onValueChange-callback is only a log. As a switch is a controlled component, I thought the UI is only rerendered when the value prop changes.
Despite my assumptions the switch shows a quick animation of toggling to false and then jumps back to true. Why is that happening? Does it have to do with the iOS specific component that is used by ReactNative?
import React from "react";
import { Switch, View } from "react-native";
export const SwitchCell = () => {
return (
<View>
<Switch onValueChange={() => console.log("Test")} value={true} />
</View>
);
};
If you want to block the event there is a prop for switch called "onChange" where as parameter you will receive the event and the new value, you can execute a check to decide if will be necessary to set the new property o if it won't change.
In case you doesn't want to change the value of switch you have to call the methods "preventDefault()"

How do I make a TextInput editable and focus on it in the same go?

I have the following code:
const handleClick = () => {
setToggleEditable(true);
textInput.current.focus();
}
A button triggers this when pressed, setting the TextInput with the ref "textInput" to be editable, and then tries to focus on it. However, it never focuses on it the first time the button is pressed. I think that is because the TextInput hasn't finished being set as editalbe, so it can't focus on it. Is there any way I can change this?
I had a similar problem, I fixed it by waiting a little (100-150 ms) after button was pressed and then focusing on textinput
focusOn = (ref: TextInput) => {
setTimeout(() => ref.focus(), 100)
}

React Native FlatList Not Re-Rendering after Asyncronous operation

I have an async function like this:
getDeals() {
if(this.props.user) {
this.setState({loading: true});
this.setState({deals: []});
var parameters = {
zip: this.props.user.zip,
sort: 'All',
category: this.props.selectedCategory,
company: this.props.user.company,
page: null,
user: this.props.user,
search: null
}
axios.post(`${constants.api}/grab-deals/`, parameters)
.then((response) => {
this.setState({totalDeals: response.data.length});
this.setState({deals: response.data, loading: false, refreshing: false});
this.forceUpdate();
})
}
}
And a FlatList component Like this:
<FlatList data={this.state.deals} style={{flex: 1, padding: 10}} extraData={this.state} keyExtractor={this.keyExtractor} renderItem={this.renderDeal.bind(this)} />
Here is the keyextractor:
keyExtractor = (item, index) => item.id;
When I call this.getDeals() the first time it works great. However when I call it a second time the axios call get's all of the correct data, but the flat list still keeps old data (it doesn't remove items that aren't in the new call).
How do I get the FlatList to always reflect the returned data?
Call this.getDeals() in componentWillUpdate() and update props?
I believe you confussing what props and state is for. Basically state is used for things that could change during the lifecycle of the component and props are kept immutable. I use them for behavior.
Unless you are changing the parameters for the getDeals function on the second call, see that all of the properties are based on the props, which are not always updated.
RN has a method called componentWillUpdate that is triggered with the new props which you can then be used to update the component itself. If you want to keep using props in your getDeals method, you will need to check if the props have changed (this happens when the parent updates the child with new props) and then trigger again the data fetch.
If this does not help, please post more code.
According to the docs you need to set the state.selected value
By passing extraData={this.state} to FlatList we make sure FlatList
itself will re-render when the state.selected changes. Without setting
this prop, FlatList would not know it needs to re-render any items
because it is also a PureComponent and the prop comparison will not
show any changes.