I am having trouble testing a drop down populated with data from an API call in React Testing Library. Below is a CodeSandbox showing the issue
https://codesandbox.io/s/mutable-sea-wtt9u
If I change App to use a hardcoded array to populate the drop down (commented out in App component), the test passes.
Thanks
When your data comes from an asynchronous fetch call, the DOM doesn't get updated synchronously, and you have to use one of the async utilities to wait for the update. This works in your case (tested in your Codesandbox):
// import `wait` directly from React Testing Library
import { render, wait } from '#testing-library/react';
...
await wait(() => {
fireEvent.change(selectElement, { target: { value: "1" } });
expect(selectElement.value).toBe("1");
});
Here's the React Testing Library docs on async utilities: https://testing-library.com/docs/dom-testing-library/api-async
EDIT: It looks like you might have changed your CodeSandbox code. Now you need to wait for the async call to be made before firing the event, since you're fetching data on mount. I've updated my answer and made sure tests pass on your current CodeSandbox.
You need to mock your fetch events. I wrote an article on how to do that. You can find it here.
Related
I've just started working with Jest and I'm having some trouble understanding how to properly use Snapshots.
I'm currently trying to test this component.
I've successfully managed to create a snapshot, but once I go check it out it looks like this:
// Jest Snapshot v1
exports[`>>>Sidemenu --- Snapshot +++capturing Snapshot of Sidemenu 1`] = `"<div class=\\"sc-dlnjPT eFWNyw\\">Test</div>"`;
I'm suspecting this is because my component's contents only show up on the second re-render, as they need a width value, this behavior is defined in this file.
How would I tell Jest to wait for my component to re-render before creating the snapshot?
Will Enzyme even be able to get the window width value?
For async operations the tricky is waiting for loading component, or something other thing that indicates that your component finished all re-renders and is ready to be snapshoted.
Something similar to this:
test('Page rendering test.', async () => {
const pageRendering = renderer.create(<Page />);
// wait for your data to be loaded before you make your assertion.
await waitForElementToBeRemoved(screen.getByText('Loading...'));
expect(pageRendering.toJSON()).toMatchSnapshot();
});
We just start moving into React Hooks from the React life-cycle and one of the things that we noticed, is that there's no straightforward way to run something similar to ComponentWillMount for loading data before sending the rendered page to the user (since useEffect is not running on SSR).
Is there any easy way supported by React to do so?
I had the same problem. I've managed to solve it with a custom hook and by rendering application twice on the server-side. The whole process looks like this:
during the first render, collect all effects to the global context,
after first render wait for all effects to resolve,
render the application for the second time with all data.
I wrote an article with examples describing this approach.
This is a direct link to the example form article on CodeSandbox.
Also, I have published an NPM package that simplifies this process - useSSE on GitHub.
This is a little tricky but one way around is to do the fetch directly in the component as in
function LifeCycle(props){
console.log("perform the fetch here as in componentwillmount")
const [number, setNumber] = useState(0)
useEffect(() => {
console.log("componentDidMount");
return () => {
console.log("componentDidUnmount");
};
}, []);
}
Edited the question after further debugging
I am having a strange issue, tried for a while to figure it out but I can't.
I have a React Component called NewGoal.jsx, after a user submits their new goal I attempt to reroute them to my "goals" page.
The problem: After they submit the browser loads in my goal page, but only for one second. It then continues and goes BACK to the NewGoal page!!
I am trying to understand why this is happening, I am beginning to feel that this might be an async issue.
Here is my code, currently it is using async-await, I also tried the same idea using a .then() but it also didn't work:
async handleSubmit(event)
{
const response = await axios.post("http://localhost:8080/addGoal",
{
goalID: null,
duration: this.state.days,
accomplishedDays: 0,
isPublic: this.state.isPublic,
description: this.state.name,
dateCreated: new Date().toISOString().substring(0,10),
}) */
// push to route
this.props.history.push("/goals");
}
While debugging, I tried taking out the functionality where I post the new message, and just did a history.push, code is below - and this completely worked.
// THIS WORKS
async handleSubmit(event)
{
// push to route
this.props.history.push("/goals");
}
But as soon as I add anything else to the function, whether before the history.push or after, it stops working.
Any advice would be very very appreciated!
Thank you
In the React Router doc's the developers talk about how the history object is mutable. Their recommendation is not to alter it directly.
https://reacttraining.com/react-router/web/api/history#history-history-is-mutable
Fortunately there are few ways to programmatically change the User's location while still working within the lifecycle events of React.
The easiest I've found is also the newest. React Router uses the React Context API to make the history object used by the router available to it's descendents. This will save you passing the history object down your component tree through props.
The only thing you need to do is make sure your AddNewGoalPage uses the history object from context instead of props.
handleSubmit(event)
...
//successful, redirect to all goals
if(res.data)
{
this.context.history.push("/goals")
}
...
})
}
I don't know if you're using a class component or a functional component for the AddNewGoalPage - but your handleSubmit method hints that it's a member of a Class, so the router's history object will be automatically available to you within your class through this.context.history.
If you are using a functional component, you'll need to make sure that the handleSubmit method is properly bound to the functional component otherwise the context the functional component parameter is given by React won't not be available to it.
Feel free to reply to me if this is the case.
So I am trying to migrate an existing Portal implementation from the old unstable_renderSubtreeIntoContainer to the new portal implementation.
I have an issue though, the relevant code has the following functionality:
unstable_renderSubtreeIntoContainer(
this,
this.props.children,
this.portalElement,
() => {
if (this.props.isOpen) {
this.props.onRender(this.portalElement,
this.getTargetElement());
}
callback(); //runs this.props.open() if the update ran open
},
);
Some of the open/close logic could be simplified by wrapping the component to be rendered inside an object and the appropriate callbacks could be called from there. But it seems createPortal has no callback to allow you specify when a render has or hasn't taken place. Is there anyway to synchronously or asynchronously on a createPortal call has finished rendering?
We're developing a search engine inside an app with Apollo and we do not know exactly how to develop a real-time search engine that makes a request to the server on every keyboard press.
On the documentation it says that we must use the new <Query /> component, but I see that this case mostly fits with firing a manual query: https://www.apollographql.com/docs/react/essentials/queries.html#manual-query
I don't know if I'm correct, or maybe we should use it in another way.
thanks!
As it is said at a link you shared, if you wanted to delay firing your query until the user performs an action (your case), such as clicking on a button, you want to use an ApolloConsumer component and directly call client.query() instead.
Query component can't be used in this situation because when React mounts a Query component, Apollo Client automatically fires off your query during rendering.
UPDATE
With Apollo Client V2.6, it is now possible to make a Query to the server manually using a hook. The hook that you want is useLazyQuery.
You'd have something like this;
const [onSearch, { called, loading, data }] = useLazyQuery(
SEARCH_QUERY,
{ variables: { searchText: state.searchText } }
);
Then you can call the onSearch function whenever your text changes inside a useEffect like below.
useEffect(() => {
onSearch()
}, [state.searchText])
Note that you might want to dounce your onSearch function such that you don't hammer your server on every key stroke.