I am building a form to confirm the pin code of telephone number after user entered it.
I started code with
const [code, setCode] = useState("");
and I use Smooth pin code input like that
<SmoothPinCodeInput
cellStyle={{
width: 50,
borderBottomWidth: 2,
borderColor: "gray"
}}
cellStyleFocused={{
borderColor: "black"
}}
animated
onFulfill={() =>
{
setIsCodeCompleted(true)
confirmUserPin()
}
}
value={code}
onTextChange={code => setCode(code)}
/>
the problem is that setCode(code) works after confirmUsePin() that is called with onFilfill() so my pin code is 4 numbers but only 3 numbers get sent, this happend because setCode doesn't update code immediately.
I solved it, I used useEffect so every time length of code is 4 it calls the function. Here is how
useEffect( () =>{
if(code.length == 4){
confirmUserPin()
}
}, [code] )
Related
I am working on a project that uses Google autocomplete to set locations. The project allows users to set pickup and destination location, and then they can also enter stop-by places up to additional 3, making it a total of 5.
Here's my sample code:
const placesRef = useRef([]);
const [stopspots, setStopSpots] = useState([]);
const [state, setState] = useState({
defaultPlacesInput: 'flex',
//and others
});
useEffect(() => {
placesRef.current = placesRef.current.slice(0, 5);
}, []);
const placesComponent = (i, placeholder) => {
return (<PlacesFrame key={i}>
...
<GooglePlacesAutocomplete
placeholder={placeholder}
minLength={2}
ref={el => placesRef.current[i] = el}
onPress={(data, details = null) => {
placesRef.current[i]?.setAddressText(data?.structured_formatting?.main_text);
setState({...state, defaultPlacesInput: 'flex'})
}}
enablePoweredByContainer={false}
fetchDetails
styles={{
textInput: [styles.input1,{paddingLeft:30}],
container: [styles.autocompleteContainer,{display:placesRef.current[i]?.isFocused() ? 'flex' : state.defaultPlacesInput}],
listView: styles.listView,
listView: styles.listView,
row: styles.row,
predefinedPlacesDescription: {
color: '#1faadb',
},
}}
query={{
key: GOOGLE_PLACES_API_KEY,
language: profile.language,
components: 'country:' + profile.iso,
}}
textInputProps={{
//value: '',
onChangeText: alterOtherFields
}}
renderRow={(data) => <PlaceRow data={data} />}
/>
...
</PlacesFrame>)
}
const stopByLocation = () => {
var counter = stopspots.length, obj = placesComponent(counter + 2, 'Drop off location');
setStopSpots([...stopspots, {
id: counter,
place: obj
}
])
}
And here is how the autocomplete component is rendered
return(
...
<View>
{placesComponent(0, 'Pick up location')}
{placesComponent(1, 'Drop off location')}
</View>
...
)
The output look like this
Everything works perfect when I call the placesComponent() function directly. But like I mentioned earlier, I want the users to be able to add up to 3 additional stop by locations, and because it is optional, additional fields is added by appending to hook, and then rendered. the code looks like this.
return(
...
<View>
{placesComponent(0, 'Pick up location')}
{placesComponent(1, 'Drop off location')}
//This will append more placed fields
{stopspots != '' ?
stopspots.map((item : {}) => ((item.place)))
: null}
<ClickableButton>
<TouchableOpacity activeOpacity={0.6} onPress={() => stopByLocation()}><AddPlaces><AntDesign name="plus" size={10} color="#444" /> Add</AddPlaces></TouchableOpacity>
</ClickableButton>
</View>
...
)
The outcome looks like this
I observed that each component binded to the hooks takes the early properties, and does not effect additional changes. While the first two fields rendered by calling the function directly does.
When I make changes to state.defaultPlacesInput (observe this in styles property of GooglePlacesAutocomplete), the changes only effect on the two components called directly.
Is there a module, or a systematic way to append the renderer function call, without using useState hooks to append the 3 additional fields?
Is it possible to expose stored properties in useState hooks to respond as the other two which observe the state changes? If yes, how?
Any contribution, suggestion will be accepted
My code is the following:
function ArScreen({ navigation }) {
const [open_tabLugar, setOpen_tabLugar] = useState(false);
const [titulo, setTitulo] = useState('');
const updateAsyncStorage = async () => {
AsyncStorage.getItem('data')
.then((res) => {
//console.log({ res : JSON.parse(res) });
console.log(JSON.parse(res).open_tabLugar);
console.log(JSON.parse(res).titulo);
setOpen_tabLugar(JSON.parse(res).open_tabLugar);
setTitulo(JSON.parse(res).titulo);
console.log(titulo);
console.log(open_tabLugar);
});
}
useEffect(() => {
updateAsyncStorage()
const interval=setInterval(()=>{
updateAsyncStorage()
},10000)
return()=>clearInterval(interval)
}, []);
Output:
console.log(JSON.parse(res).open_tabLugar); ====== true
console.log(JSON.parse(res).titulo); ===== hello
after setOpen_tabLugar and setTitulo
console.log(titulo); ===== ""
console.log(open_tabLugar); ===== false
Why does that happen? I am changing the state (or trying to change it), but it doesn't work. What am I doing wrong?
If you notice, I have a bucket that updates every 10 seconds in useEffect. But, that's not the problem, that works fine (in the update I retract myself, that is the problem, only I don't know how to solve it). The problem is in the change of states.
What am I doing wrong?
UPDATE 1:
If I put this:
setTitulo("HEEEEEEEEEEEEY");
console.log(titulo);
Doesn't work, keeps coming up blank.
UPDATE 2:
I think the problem is inside useEffect(() => {
Because I just created a button to run updateAsyncStorage manually and it did work.
<TouchableOpacity
style={{width: 50, borderRadius: 50, height: 50, backgroundColor: 'white', alignItems: 'center', justifyContent: 'center'}}
onPress={() => updateAsyncStorage()} >
<Text style={{color: 'black', fontSize: 20, fontWeight: 'bold'}}>Load</Text>
</TouchableOpacity>
The button does work fine. Yes the state changes. How do I make it work automatically with useEffect or another alternative?
UPDATE 3:
My new code:
const [open_tabLugar, setOpen_tabLugar] = useState(false);
const [tabLugar, setTabLugar] = useState(false);
useEffect(() => {
AsyncStorage.getItem('data')
.then((res) => {
//console.log({ res : JSON.parse(res) });
console.log(JSON.parse(res).open_tabLugar);
console.log(JSON.parse(res).tipo);
console.log(JSON.parse(res).titulo);
console.log(JSON.parse(res).descripcion);
console.log(JSON.parse(res).distance);
console.log(JSON.parse(res).imagen);
console.log(JSON.parse(res).imagen_1);
// string to boolean in AsyncStorage
setOpen_tabLugar(JSON.parse(res).open_tabLugar);
setTipo(JSON.parse(res).tipo);
setTitulo(JSON.parse(res).titulo);
setDescripcion(JSON.parse(res).descripcion);
setDistance(JSON.parse(res).distance);
setImagen(JSON.parse(res).imagen);
setImagen_1(JSON.parse(res).imagen_1);
if(open_tabLugar){
console.log('-------------- OPEN MODAL --------------');
setTabLugar(true);
setOpen_tabLugar(false);
console.log(open_tabLugar);
}
});
},[open_tabLugar, tipo, titulo, descripcion, distance, imagen, imagen_1]);
return(
...
<Modal animationType="slide" transparent={true} visible={tabLugar}>
<View style={styles_modal.modalViewTabL}>
<TouchableOpacity onPress={() => setTabLugar(!tabLugar)} style={[styles_modal.button, styles_modal.buttonClose]} >
<Text style={styles_modal.buttonClosetextStyle}>X</Text>
</TouchableOpacity>
<Text style={styles_modal.place_description} numberOfLines={7}>
La Casa de la Cultura es una de las más importantes obras de arte de la ciudad de Madrid.
</Text>
</View>
</Modal>
That works halfway, the problem is that it runs infinitely, it has no end. That started when I put: [open_tabLugar, type, title, description, distance, image, image_1]
After I open my modal with setTabLugar(true); I write setOpen_tabLugar(false);
That should prevent it from opening it again, but then again it runs as true, infinitely.
UPDATE 4:
My error is in the following snippet. Since it runs endlessly. If I remove it the loop stops. How do I fix that problem? Remember that I want the modal to open, but not infinitely in a loop.
if(open_tabLugar){
console.log('-------------- OPEN OPEN OPEN TAB LUGAR --------------');
setTabLugar(true);
setOpen_tabLugar(false);
console.log(open_tabLugar);
}
Unfortunately, hooks cannot be used this way. After the set operation is done, the rendering process takes place and the new value you have assigned will then be available. If you have processes that will work according to these values, you should create useEffect for this and give the variable you will use to the dependency part.
For example, you can print the current values in the following way.
useEffect(() => {
console.log(titulo);
console.log(open_tabLugar);
},
[titulo, open_tabLugar],
);
You can show it through a real example for better help.
I have this code to update some TextInputs displayed with a list (AccordionList) in Redux. But the value makes 2 seconds to refresh after I type some text and leave the field (onBlur).
Example : I type my text, I leave the field, it puts the previous value and then, after 2 seconds, the value is changed with the new one.
I think it's not the best way to do that but I don't know how to make this different.
Here is the code :
const PieceForm = (props) => {
//Get from Redux Store for default value of each TextInputs
const [remarques, setRemarques] = React.useState(getRemarques);
//Used to change the text
const [text, onChangeText] = React.useState("");
....
//Used to update my remarques on Store and State when leave the field (onBlur)
function updateRemarque(review) {
//Send to the store
let valueToSend = [{
text: text,
review: review,
PieceId: piece.id,
LieuId: lieuId
}]
const action = {type: "UPDATE_REMARQUE", value: valueToSend}
props.dispatch(action)
//Update remarques state to have the value on the TextInput
let newRemarque = [...remarques]
newRemarque[review.id] = text
setRemarques(
newRemarque
)
}
//Render my fields from AccordionList
const _body = (item) => {
return (
<View style={[{padding: 30}]}>
//My TextInput with default value from remarques state
<TextInput
placeholder='Remarques complémentaires'
style={[GLOBAL.selectLegend]}
multiline={true}
onChangeText={onChangeText}
onBlur={() => updateRemarque(item)}
value={remarques[item.id]}
/>
</View>
);
}
return(
//Render The content (Like a FlatList)
<AccordionList
nestedScrollEnabled
data={reviews}
keyExtractor={item => `${item.id}`}
body={_body}
ListFooterComponent={_footer}
contentContainerStyle={{
paddingBottom: 15,
paddingTop: 30
}}
/>
...
Dont use onBlur thats what is slowing you down. Choose to either use state or redux for the textfields and change that with onChange callback. If you want it in redux, just fire the action on text change and make value equal to the redux value.
I have an array of "favorite" listings per user. I want to set a state variable depending on if it is rendering a favorite item or a regular (non-favorite) item. This is a little heart icon that is either filled or unfilled. It is incorrectly setting the state on first render. Correctly sets it after refresh.. I'm doing something wrong and have tried many things to no avail so if someone could just look at this code and tell me if you see any glaring flaws. If not then I will look elsewhere for the problem.
Behavior when app loads: I am doing console.log just after the state is set to show whether it's a favorite or not, and the contents of the favorite state variable (an image url, but in the console it shows it as either 21 for non-fav, or 22 for a fav). I can see that it is correctly pulling the array of favorites, and correctly identifying those that are and are not favorited (1 means its a favorite). It is however not setting the state variable correctly. Furthermore, it IS setting it correctly for only some of the listings. Currently I have all listings marked as a favorite, and it only messes up the first 10. After that they all set right. MORE bizarre, is upon refreshing the screen, it correctly sets all of them.
MainHeader.js (pulling data from db, setting initial array of favorite listings, and passing it to the messagesScreen component)
const [favsArray, setFavsArray] = useState("");
useEffect(() => {
lookupUser()
.then((snapshot) => {
if (snapshot.hasChildren()) {
snapshot.forEach(function(childSnapshot) {
let favs = childSnapshot.child("favorites").val();
setFavsArray(favs);
})
}
})
.catch((error) => {console.error('Error:', error)});
}, []);
return (
<NavigationContainer>
<View style={styles.headerContainer}>
<Image
style={styles.image}
source={require("../assets/newheader4.png")}
/>
</View>
<Tab.Navigator
tabBarOptions={{
activeTintColor: "blue",
inactiveTintColor: "black",
style: {},
tabStyle: {
width: "auto",
backgroundColor: "#e0d5f3",
borderTopWidth: 3,
borderBottomWidth: 3,
borderRightColor: "gray",
},
labelStyle: {
fontSize: 14,
fontWeight: "bold",
},
scrollEnabled: true,
}}
>
<Tab.Screen name="All Deals" children={()=><MessagesScreen favsArray={favsArray} setFavsArray={setFavsArray}/>} />
</Tab.Navigator>
</NavigationContainer>
MessagesScreen, receives favsArray and renders a FlatList with component Card which it feeds favsArray to.
<FlatList
data={messagesShow}
keyExtractor={(messagesShow) => messagesShow.id.toString()}
renderItem={({ item }) => (
<Card
price={item.currentPrice}
title={item.title}
image={item.image}
posted={item.postedDate}
discAmount={item.discountAmount}
discType={item.discType}
expiration={item.expiration}
promoCode={item.promoCode}
affLink={item.amzLink}
indexStore={item.indexStore}
store={item.store}
favsArray = {favsArray}
/>
)}
ItemSeparatorComponent={ListItemSeparator}
contentContainerStyle={styles.messagesList}
refreshing={refreshing}
onRefresh={() =>
db.ref('deals').once('value', (snapshot) =>{
let testData = [];
snapshot.forEach((child)=>{
// if (child.val().hasOwnProperty('title')){
testData.push({
id: child.key,
title: child.val().hasOwnProperty('title') ? child.val().title : 'NA',
currentPrice: child.val().price,
discountAmount: child.val().discAmt,
discType: child.val().discType,
promoCode: child.val().promoCode,
expiration: child.val().expDate,
postedDate: child.val().postDate,
image: { uri: child.val().imageLink},
amzLink: child.val().affLink,
category: child.val().category,
indexStore: child.val().indexStore,
store: child.val().store
})
// }
checkMessages(testData);
})
})
.then()
.catch((error) => {console.error('Error:', error)})
}
/>
Card component, this is in a FlatList where favsArray is passed as a prop (correctly verified by console), along with the individual listing data. If it finds the listing in the fav array, it should set to HeartFilled (1), if not set to HeartEmpty (0).
let test = [];
test = favsArray.split(',');
let isFav = 0;
let found = test.find(function (element) {
return element == indexStore;
});
if (found != undefined){
isFav = 1;
}
const [heartFilled, setHeartFilled] = useState( isFav == 1 ? require('../assets/heartFilled.png') : require('../assets/heartEmpty.png'));
console.log(isFav + ' ' + heartFilled);
Looking at my console, you can see it correctly shows each listing as a favorite, but for the first 10 listings it sets the state to the wrong image (21, shown in red). These should all be 22.
In my app, I have a function which calls every 2s a bluetooth command to ask the current temperature of a device with a setInterval function.
The bluetooth response is given by monitored function. I use react native-ble-plx library for that.
I have no problem with this process.
The temperature is returned via a property which is dispatched via redux in an action file.
But when I "dispatch" (via redux) the function to my screen, I have a short interrupt which causes a laggy/jerky behavior. In my case, I have a slide to unlock button, and on my device when the dispatch is call, the touch operation is interrupted, and become not intuitive and annoying. It's difficult to explain the problem, but my question is simple, how I have to set react-redux not to be laggy, or not interrupt current user interaction on redux dispatch ?
My app, is based on this project structure (for react-redux with Ble) : https://github.com/momolarson/BLEServiceDiscovery
Environement:
react-native: 0.63.3
react-native-ble-plx: 2.0.2
react-redux: 7.2.1
This is pseudo code of my app (the code is more longer, but I have excluded all other by remove them) :
HomeScreen.js
import stuff[...]
class HomeScreen extends Component {
componentDidMount() {
this.timer = setInterval(() => {
this.props.readTemp();
}, 2000);
}
render() {
const { value } = this.state
return (
<>
<ScrollView>
<Text>{this.props.temperatture}"></Text>
<Slide2Unlock/>
</ScrollView>
</>
);
}
}
function mapStateToProps(state) {
return {
temperature: state.temperature,
};
}
const mapDispatchToProps = dispatch => ({
readTemp: () => bluetooth.readTemp(),
})
export default connect(mapStateToProps, mapDispatchToProps())(HomeScreen);
redux's action file : actionBt.js (my file is based on this https://github.com/momolarson/BLEServiceDiscovery/blob/master/actions/index.js)
[...]
device.monitorCharacteristicForService(
characteristicData.serviceUUID,
characteristicData.uuid,
(error, characteristic) => {
if (characteristic != null && characteristic.value != null) {
dispatch(formatTemperature(characteristic.value));
}
},
);
thanks for your help
Update 1
I make a specific version of my app, without bluetooth, just the slide to unlock module and a watcher with setInterval, and still have a laggy behavior, when the state is dispatched. I have done tests with button only, when I tap then show the value via dispatch, it's still the same trouble.
this my test code, index.js (redux action file)
export const readTemp = () => {
return (dispatch, getState, DeviceManager) => {
const state = getState();
console.log("READ TEMP");
dispatch(temperatureSensor( Math.random(0,9) ))
}
}
function BLEservices(BLEServices) {
setInterval(() => {
BLEServices.readTemp();
}, 2500);
return (
<SafeAreaView style={styles.container}>
<Slider
childrenContainer={{ }}
onEndReached={() => {
console.log('REACHED')
}}
containerStyle={{
height:40,
margin: 8,
backgroundColor: "#EEEEEE",
overflow: 'hidden',
alignItems: 'center',
justifyContent: 'center',
width: '50%',
}}
sliderElement={
<Text style={{color:"#FFF"}}>TEST</Text>
}
>
<Text style={{color: "#D5BD9E"}}>unlock</Text>
</Slider>
<Text>Temperature: {BLEServices.temperatureSensor}</Text>
</SafeAreaView>
);
}
thanks for your advice, and your help
Update 2
Solution found, see my answer below. The problem was type of var user in dispatch and some side effect due to previous test I have done on app and not clean them.
I solved my problem, by finding multiple var who are contains objects. I have a var which contain four attributes, I update and use one of them. And this object was update by my watcher. When I dispatch object to get a part of this object, I have to read the whole object, and this one is fully updated by my watchern which cause laggy render. So i have splitted that, to update only per var.
Another thing I've done, I split my interface elements in multi component, before, I has a lot of code in one screen, because I didn't need to reuse them elsewhere.