State change detected only when screen is tapped - reactjs

I have a few TouchOpacity components which fire a function when pressed. This function retrieves data and then sets state.
const Summary = () => {
const [timeSpan, setTimeSpan] = useState('Day');
const [derivedData, setDerivedData] = useState({
chartTimeSpan: 'Day',
sales: [],
totalSales: 0,
orders: 0,
logins: 0,
});
const _fetchSummary = async (timeSpan) => {
console.log(`_fetchSummary HIT : ${timeSpan}`);
try {
const res = await axios.get(`/summary/${timeSpan.toLowerCase()}`);
const { loginCount, orderQty, sales, totalSales } = res.data;
await setDerivedData({
chartTimeSpan: timeSpan,
sales,
totalSales,
orders: orderQty,
logins: loginCount,
});
await setTimeSpan(timeSpan);
console.log(timeSpan, loginCount, orderQty, sales, totalSales);
} catch (err) {
console.log(err);
}
};
const _switchTimeSpan = (newTimeSpan) => {
console.log(`TimeSpan : ${timeSpan}`);
console.log(`NewTimeSpan : ${newTimeSpan}`);
if (timeSpan !== newTimeSpan) {
_fetchSummary(newTimeSpan);
}
};
const { chartTimeSpan, sales, totalSales, orders, logins } = derivedData;
console.log(derivedData);
return (
<>
<TouchableOpacity onPress={() => _switchTimeSpan('Day')}>
<Text style={dropDownItemStyle}>Day</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => _switchTimeSpan('Week')}>
<Text style={dropDownItemStyle}>Week</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => _switchTimeSpan('Month')}>
<Text style={dropDownItemStyle}>Month</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => _switchTimeSpan('Year')}>
<Text style={dropDownItemStyle}>Year</Text>
</TouchableOpacity>
</>
);
};
Everything works fine. The data gets fetched when i click the buttons too. However state doesn't get updated after the data is fetched. I know this because console.log(derivedData); just above the return statement doesn't run. When i tap anywhere on the screen the console.log(derivedData); gives the expected output. Note that i have not set any function which detects this event when i touch the screen.
I have used the derivedData in some other components but did not include those for simplicity sake.

The console.log(derivedData) will be run, when the components need to be rerendered. This depends on the injected state and props variables. Since state variables are not used in JSX, there is no need to rerender the component and log the new derivedData.
You could bypass this by using import { useEffect } from 'react'. Try to log derived data with:
useEffect(() => {
console.log(derivedData);
});

This issue was caused because I have included the following lines of code in App.js file.
XMLHttpRequest = GLOBAL.originalXMLHttpRequest
? GLOBAL.originalXMLHttpRequest
: GLOBAL.XMLHttpRequest;
// fetch logger
global._fetch = fetch;
global.fetch = (uri, options, ...args) => (global._fetch(uri, options, ...args)
.then((response) => {
console.log('Fetch', { request: { uri, options, ...args }, response });
return response;
}));
The above code allows to detect the network requests. I don't know why but although the above code shows the network requests it also delays the data passed from requests to go into the component unless the screen is tapped.
Commenting out the above lines in production lets the code work as it should.

Related

React > expo > Axio > Async do not wait for response

I am a real dummy on expo and react.
I am trying to fetch infos API using AXIO.
My problem is that before the API request is finish, expo is asking for a return statement.
Here is my code :
const sendGetRequest = async () => {
try {
const resp = await axios.get(uri, {
auth: {
username: '*****',
password: '****'
}})
console.log(resp.data);
return (
<View style={styles.container}>
<Text style={styles.title}>Action</Text>
<View style={styles.separator} lightColor="#eee" darkColor="rgba(255,255,255,0.1)" />
<Text>
Order ID: {resp.data.id}
Nom client: {resp.data.billing.first_name} {resp.data.billing.last_name}
</Text>
<EditScreenInfo path="/screens/TabTwoScreen.tsx" />
</View>
);
} catch (err) {
// Handle Error Here
console.error(err);
}
};
sendGetRequest();
ANd I get this error :
" Nothing was return from render "
I guess that the await is not really awaiting the result of the async..
Can you help me to see where is my mistake???
Thanks you so much..
Regards,
Pierre
Pierre, try to add await before calling sendGetRequest();
Like this:
await sendGetRequest();
ok I found the solution to my problem.
I spent couple of usefull minutes to read more about promises and callbacks.
I was wrong thinking that a promise will hold the code until it get a response. When u understand that a promise send a callback once realized the right code is as following :
// Creating a "DOM listener" -> sorry about vocabulary
const [myText, setMyText] = useState("My Original Text");
// Creating the async/awaiting function
const sendGetRequest = async () => {
try {
var resp = await axios.get(uri, {
auth: {
username: '*******',
password: '*******'
}});
await setMyText('Nom du client:'+ resp.data.billing.first_name);
} catch (err) {
// Handle Error Here
console.error(err);
}
};
sendGetRequest();
// AND RETURN THE HTML AS FOLLOW
<View>
<Text onPress = {() => setMyText("My Changed Text")}>
{myText}
</Text>
</View>

Pass data of array to react native view

I have a function that fetch the data from backend and map over the data and then add them to array
called events_data
function getvals() {
return fetch('http://**********/users/timetable')
.then((response) => response.json())
.then((output) => {
addData(output, events_data);
})
.catch(error => console.log(error))
}
function addData(data, data2) {
data.map((d) => {
data2.push({
title: d.name,
startTime: genTimeBlock(d.day, d.start_time),
endTime: genTimeBlock(d.day, d.end_time),
location: d.location,
extra_descriptions: [d.extra_descriptions],
});
});
}
So in my app view I want to pass events_data to events props:
<SafeAreaView style={{ flex: 1, padding: 30 }}>
<View style={styles.container}>
<TimeTableView
scrollViewRef={this.scrollViewRef}
events={// events.data will be passed here as array format //}**
pivotTime={8}
pivotDate={this.pivotDate}
numberOfDays={this.numOfDays}
onEventPress={}
headerStyle={styles.headerStyle}
formatDateHeader="dddd"
locale="en"
/>
</View>
</SafeAreaView>
Side note: the timetable view it is a third party package that accept array passed in porp events={} and display its data in timetable format
so here I want to pass events_data array coming from function addData and pass it to events prop in <TimeTableView>
function getvals() {
return fetch('http://**********/users/timetable')
.then((response) => response.json())
.then((output) => {
return addData(output, events_data); //<-- add a return statement.
})
.catch(error => console.log(error))
}
in your class component where you are calling the get Val function.
const data = getvals();
this.setState({ events: data });
then you can use this.state.events in your table.

How to update state of messages in react-native-gifted-chat?

I relying on API calls for sending messages using react-native-gifted-chat in a react native app, I want the flow to be like when the user clicks on the send button it should have the state as pending:true and when the API call is a success I want it to have it as pending:false,sent:true I can achieve the first part but even when the API call is a success it does not update the state. Below is my implementation of it, I am trying to follow solution posted [here][1] but I think something is wrong with my implementation, Please let me know if any clarification is required.
function SmsConversations({ route }) {
const onSend = useCallback((messages = []) => {
const [messageToSend] = messages;
messageToSend.pending = true;
sendSMS(toNumber, fromNumber, messageToSend.text)
.then((res) => {
messageToSend.pending = false;
messageToSend.sent = true;
})
.catch((err) => (messageToSend.pending = true))
.done();
setMessages((previousMessages) =>
GiftedChat.append(previousMessages, messageToSend)
);
}, []);
return (
<GiftedChat
messages={messages}
onSend={(messages) => onSend(messages)}
user={{
_id: 1,
}}
/>
);
}
Also if the API is errored is there a way to handle it using react-native-gifted-chat, like failed or something?

How can i request remote data after press enter? (mbrn/material-table)

I'm using remote data example from material table, The current behavior
In componentDidMount the data request by default.
any search or sorting make by default another request to get data based on the new query
I can delay the request by providing debounceInterval
What I want to do?
I want when Itype in the global search----> I don't want to get data by default unless I press enter
And here is my render method that will make the table resolves the remote data once it's received the data
<Entity
storeId={storeId}
entityRef={ref => { this.entity = ref; }}
onEntityReceived={data => this.onEntityReceived(data)}
onEntityReceivedError={data => this.onEntityReceivedError(data)}
render={store => (
<React.Fragment>
<If condition={this.exceptionError}>
<Message variant={'warning'} text={this.exceptionError} />
</If>
<MaterialTable
tableRef={ref => this.tableRef = ref}
title={this.title}
data={query => {
this.get(query);
return new Promise(resolve => event.on('data-fetched', resolve));
}}
isLoading={(store.loading && this.exceptionErrorsLoader) || isLoading}
options={this.options}
actions={this.actions}
localization={this.localization}
columns={this.columns}
components={this.components}
icons={this.icons}
detailPanel={this.rowDetailsPanel}
onRowClick={onRowClick}
/>
Here is the code that will handle received data to provide it to the table
onEntityReceived(data) {
this.exceptionErrorsLoader = false;
event.notify('data-fetched', {
page: this.state.pageIndex,
totalCount: data.totalCount,
data,
});
}
This is the get method that will get the data from server
get(query) {
const { oldQuery } = this.state;
const { additionalEntityPayload } = this.props;
const serverSideLink = this.getServerSideLink(query);
this.exceptionErrorsLoader = true;
this.setState({
query,
// ======== In Order To Save FIRST_QUERY (in case we need to refresh old data)
oldQuery: isEmpty(oldQuery) ? query : oldQuery,
pageIndex: query.page,
pageSize: query.pageSize,
}, () => {
if(!isEmpty(additionalEntityPayload)) {
return this.entity.get({
serverSideLink, additionalPayload: additionalEntityPayload });
}
this.entity.get({ serverSideLink });
});
}
The issue is I don't know how to control the search field or other field because they are not exposed
Thanks in Advance.

How to properly fetch data from async storage in react native?

I am attempting to load and retrieve local data through asynchronous storage. I've laid out the current set up on a page, but am unable to retrieve data. Data is definitely saved as I receive a notification about it once I click store data. But when I proceed to call data it returns a null value. What am I doing something wrong here?
const STORAGE_KEY = '#save_enableauto'
state={
enableAuto:false
}
_storeData = async enableAuto => {
try {
let x = toString(enableAuto);
await AsyncStorage.setItem(STORAGE_KEY, x)
alert('Data successfully saved!')
//this.setState({ enableAuto: x })
} catch (e) {
console.log(e)
alert('Failed to save name.')
}
}
_retrieveData = async () => {
console.log('trying to call data')
try {
const enableAuto = await AsyncStorage.getItem('STORAGE_KEY');
console.log(enableAuto)
if (enableAuto !== null) {
// We have data!!
console.log(enableAuto);
}
} catch (error) {
alert('failed to load previous settings.')
// Error retrieving data
}
};
...
<Button title={'Store Data'} onPress={() => this._storeData(this.state.enableAuto)}/>
<Button title={'call Data'} onPress={() => this._retrieveData()}/>
</View>
<View>
<CheckBox
value={this.state.enableAuto}
onValueChange={() => this.setState({ checked: !this.state.enableAuto })}
/>
The error above is while you are storing, you are setting :
await AsyncStorage.setItem(STORAGE_KEY, x)
and here STORAGE_KEY refers to value #save_enableauto
But while you are retrieving data
const enableAuto = await AsyncStorage.getItem('STORAGE_KEY');
you are giving 'STORAGE_KEY' as string , so that means its refering 'STORAGE_KEY' and it doesnt have any stored value. Try this
const enableAuto = await AsyncStorage.getItem(STORAGE_KEY);
Hope it helps. feel free for doubts

Resources