Code executes in componentDidMount but not in Other functions - react-native

So I've run into an weird issue, maybe I'm doing something wrong here but I haven't had this problem before and my app is full of similar code.
Basically I'm trying to do a simple Firestore get() in a function attached to a button onPress. For some reason the code is not run, or if it is I'm not getting any feedback. If I put the same code in componentDidMount it runs and gives me the database snapshot.
Here are the two bits of code in question.
updated for the current binding I am using
this.usersRef = firebase.firestore().collection('users');
componentDidMount() {
this.usersRef
.get()
.then((snapshot) => {
console.log('TEST didMount snapshot', snapshot);
}).catch((error) => {
console.log(error)
});
}
submitSearch() {
console.log('submitSearch')
this.usersRef
.get()
.then((snapshot) => {
console.log('TEST submitSearch snapshot', snapshot);
}).catch((error) => {
console.log(error)
});
}
The submitSearch is called from here
<Button style={{marginTop: 3, marginBottom: 8}} primary onPress={() => this.submitSearch()}>
{strings.search}
</Button>
So I've tried a lot of things here and I can't seem to figure out why the code won't work in the submitSearch function. The only thing I see from that function is the console log saying submit search.
Any ideas would be appreciated! Thanks.

The issue was the way the function was bound.
I originally had the following in the constructor:
this.submitSearch = this.submitSearch.bind(this)
and was calling it in the button component with
onPress={this.submitSearch}
I removed the constructor binding and used just the following in the button component:
onPress={() => this.submitSearch()}
I thought I had done that already! But I can confirm this fixes the issue. It's strange that there are no warnings or errors though, seems like a scenario where they would be helpful instead of just not running the code.

Related

showing data in console but not inside modal in Vue.js

Particular user Details
invoice_info: [],
this.$http.get(`/api/invoice-generator-retrieve/${nid}/`)
.then((res) => {
this.invoice_info = res.data.data;
console.log(res.data.data, "State", this.invoice_info.invoice_generator_id)
}).catch((err) => {
console.log(err);
})
Storing below data in the invoice_info[],
invoice_generator_id:5
is_paid:false
total_amount:7000
total_tk:7150
updated_at:"2023-01-25T16:17:15.187859"
user_id:4656
<h5 class="modal-title font22think ml-4" id="exampleModalLabel" >${
this.invoice_info.user_id }</h5>
On Modal ${ this.invoice_info.user_id } is showing instead the result should be 4656
any idead to fix this issue?
Since my above code failed to work accordingly I managed them to work by getting the element by Id and inject the value.

"after all" hook error while running test

This is my test body:
/// <reference types = "cypress" />
it('Testing story book button primary', function(){
cy.visit('https://storybook.prod.ublox-website.ch4.amazee.io/iframe.html?id=components-button--primary&viewMode=story')
cy.wait(1000)
cy.eyesOpen({
appName: 'Story book',
testName: 'check button primary',
});
cy.eyesCheckWindow();
cy.eyesClose();
});
I have attached a screenshot of my error at the end it displays this error( I have attached).
Can someone please let me know why I am getting this error? I am stuck.
Thanks in advance.
It would be difficult for someone to help debug this with the limited code provided I think. Mainly because I'm not sure what your after() block looks like, the error that is being throw isn't occurring in this test, it's occurring in the teardown code when body.success doesn't exist (what results in body.success existing?). Additionally eyesOpen and eyesCheckWindow and eyesClose seem to be custom commands specific to Applitools, I'd at least recommend adding that as a tag or edit your post to include that information because that isn't part of the general testers' Cypress workflow/plugin stack.
Other than that, I'd try adding cy.log(body.error) or console.log(body.error) or adding the after() block, and add the results to your question.
On a separate note, you can try using the Applitools example test structure
it('works', () => {
cy.visit('https://applitools.com/helloworld');
cy.eyesOpen({
appName: 'Hello World!',
testName: 'My first JavaScript test!',
browser: { width: 800, height: 600 },
});
cy.eyesCheckWindow('Main Page');
cy.get('button').click();
cy.eyesCheckWindow('Click!');
cy.eyesClose();
});
});
or their "best practice" example structure
describe('Hello world', () => {
beforeEach(() => {
cy.eyesOpen({
appName: 'Hello World!',
browser: { width: 800, height: 600 },
});
});
afterEach(() => {
cy.eyesClose();
});
it('My first JavaScript test!', () => {
cy.visit('https://applitools.com/helloworld');
cy.eyesCheckWindow('Main Page');
cy.get('button').click();
cy.eyesCheckWindow('Click!');
});
});
Both look like they're passing text into eyesCheckWindow but I also am not familiar with applitools so this could be useless information.

How to properly test if a Toast has been shown in react native using native base?

I am trying to write a test that checks if the screen is showing a Toast with an error message. The test passes, but there is a warning:
console.error
Warning: You called act(async () => ...) without await.
This could lead to unexpected testing behaviour, interleaving multiple act calls
and mixing their scopes. You should - await act(async () => ...);
The screen is working fine, I am just learning how to write tests. This is my test:
it('shows error correctly', async () => {
mockAxios.get.mockRejectedValueOnce(new Error('Async error'))
const { queryByText } = renderWithRedux(<DiscoverScreen />)
await waitFor(() => {
expect(queryByText(ErrorMessages.GeneralErrorToast)).not.toBeNull()
})
await waitForElementToBeRemoved(() => queryByText(ErrorMessages.GeneralErrorToast), { timeout: 5000 })
})
What am I not doing right? Definitely there is an issue with react native testing, because there are problems for certain async querying, especially when you have several of them. I found that here: https://github.com/callstack/react-native-testing-library/issues/379#issuecomment-720734366
I am using native base for showing the Toast, which is using Animated I think. Should I use jest.useFakeTimers() and how?
After researching how the Toast in native base works (this could be done when you open the source code in github - https://github.com/GeekyAnts/NativeBase/blob/master/src/basic/ToastContainer.js), I found that it uses Animated.timing.
So I had to find out how to deal with react native animations in tests. That article had a solution that worked for me: https://medium.com/#joncardasis/react-native-how-to-test-components-implementing-animated-with-jest-8cabb5fc2730
After I added the code in my jest setup file, this is how my test looks:
global.withAnimatedTimeTravelEnabled(async () => {
const { queryByText } = renderedComponent
await waitFor(() => {
expect(queryByText(ErrorMessages.GeneralErrorToast)).not.toBeNull()
})
global.timeTravel(Constants.ErrorToastDelay)
expect(queryByText(ErrorMessages.GeneralErrorToast)).toBeNull()
})
It works and now the test passes with no warnings!
One little adjustment in my jest configuration was also missing. I had to add this:
"testEnvironment": "jsdom"
I hope this could help someone else, too!

Assert element exists after all XHR requests finished

I'm visiting a page which is fetching data Asynchronously (multiple XHR requests), and then asserting if a certain DOM element is visible/exists in the page.
So far I was only able to get the page and the data fetched with using cy.wait() either with an arbitrary time, or by aliasing the actual request, and using the promise-like syntax to make sure my cy.get() is done after the XHR response has completed.
Here is what doesn't work:
before(() => {
cy.login();
cy.server();
cy.route('/v0/real-properties/*').as('getRealPropertyDetails');
cy.visit('/real-properties/1/real-property-units-table');
});
beforeEach(() => {
Cypress.Cookies.preserveOnce('platform_session');
});
after(() => {
cy.clearCookies();
});
context('when viewport is below 1367', () => {
it('should be closed by default', () => {
cy.wait('#getRealPropertyDetails'); // the documentation says this is the way to go
sSizes.forEach((size) => {
cy.viewport(size[0], size[1]);
cy.get('.v-navigation-drawer--open.real-property-details-sidebar').should('not.exist');
});
});
Adding cy.wait(1000); in the before() or beforeEach() hooks also works, but this is not really an acceptable solution.
What works, but not sure if this is the way to do this (I would have to add this for every page, would be quite annoying) :
it('should be closed by default', () => {
cy.wait('#getRealPropertyDetails').then(() => {
sSizes.forEach((size) => {
cy.viewport(size[0], size[1]);
cy.get('.real-property-details-sidebar').should('not.be.visible');
});
});
});
I see that you have browser reloads there (beforeEach), which could potentially wipe out the route spy, but not sure why cy.wait().then would work. I would try switching from before to beforeEach though, creating things once is always trickier than letting them be created before each test

React Native: Refresh component on click button

I tried to create a chat in my application. Messages are stored in a server and I comunicate with it with an API. I tried to make it real time chat so I used componentDidUpdate shouldComponentUpdate method (componentDidUpdate and componentwillupdate too) but there is a warning that appears telling me that make my application very weak... in any case I can't do it like this.
I want suggestions to be able to display the message when the user click on the send button and how can I have a notification of receipt of new messages
in case your server is not sending notifications on new message, you can implement polling mechanism to check server for any new message after certain small time intervals.
as long as you are fetching new messages and populating them, using redux, you will only need componentwillreceiveprops method which you can use to set state of new message array.
The warning is probably coming from the "unsafe" (deprecated in React 17) method componentwillupdate. These methods (including componentwillreceiveprops as Firdous mentioned) shouldn't be used anymore in preference for methods such as componentdidupdate.
What you're looking to achieve can be done with an appropriate use of componentdidupdate and perhaps getderivedstatefromprops if needed. For instance, if you have connected your chat view (and your message text input) to redux (which should hold the messages) the componentdidupdate method will listen to incoming prop changes (from the onPress event for instance from your text input) and can therefore update the view with the new props (messages).
I provide some code to more details:
componentDidUpdate(prevProps, prevState) {
if (prevState.msg !== this.state.msg) {
this.getMsgFromApi(this.state.userId).then(data => {
this.setState({ msg: data})
});
}
};
and to display messages when component will mount i do this:
componentWillMount() {
AsyncStorage.getItem(USERID_STORED)
.then(userIdStore => {
this.setState({ userId: userIdStore});
this.getMsgFromApi(this.state.userId).then(data => {
this.setState({ msg: data})
});
});
};
all message are dislaied in a flatlist
<View style={Styles.messagesContainer}>
<FlatList
style={Styles.flatlist}
data={this.state.msg.Conversation}
keyExtractor={(item) => item.datetime.toString()}
renderItem={({item}) => <MsgR msg={item}/>}
inverted= {-1}
/>
</View>
and I do a condition to differentiate if it is a user message or a received message because the API that handles the messages puts them like this :
"Conversation": [
{
"adherent": "received message",
"user": null,
"datetime":"2019-07-09
09: 42: 55"
},
{
"adherent":null,
"user":"user message",
"datetime":"2019-07-04 06: 14: 18"
}
2","user":null,"datetime":"2019-07-03 12: 34: 10"
},
]
display of messages according to the sender:
const msg = this.props.msg;
return (
<View style={[Styles.container, msg.user===null ? Styles.right : Styles.left]}>
<Text style={msg.user===null ? Styles.textMsgRight : Styles.textMsgLeft}>{msg.user===null ? msg.adherent : msg.user}</Text>
</View>
);
I found the solution by adding extraData flatlist prop:
<FlatList
style={Styles.flatlist}
data={this.state.msg.Conversation}
keyExtractor={(item) => item.datetime.toString()}
extraData={this.state} //this prop
renderItem={({item}) => <MsgR msg={item}/>}
inverted= {-1}
/>
and when I click the send button, I re-call getMsgFromApi() method:
onPressSend() {
AsyncStorage.getItem(USERID_STORED)
.then(userIdStore => {
this.setState({ userId: userIdStore});
this.sendMsgS(this.state.userId).then(() => {
this.setState({msgS: ''});
this method --> this.getMsgFromApi(this.state.userId).then(data => {
this.setState({ msg: data,});
});
});
});
}
and it's work fine: when I click on send button, I have the message sent displayed after a few ms, it's unpleasant but it works !