react 16 and react router 4, how to test if defined for route component was rendered? - jestjs

I have an application based on react-boilerplate. I updated it lately to the newest version, which includes react-router 4.2 and also bumped react to version 16.
Now I work on aligning routes in application to the newest version and also want to write tests for routing.
I have main file App.js
export default () => (<Switch>
<Route path="/offer" component={Offer} />
<Route path="" component={NotFoundPage} />
</Switch>);
And I wrote test:
describe('<App />', () => {
it('should render proper component, based on routing', () => {
const wrapper = shallow(
<MemoryRouter initialEntries={['/offer']} initialIndex={0}>
<App />
</MemoryRouter>
);
expect(wrapper.find(Offer)).toBePresent();
});
});
I installed enzyme-matchers to make toBePresent work, but I'm getting an error: TypeError: expect(...).toBePresent is not a function.
So probably you know any other way to test with jest if on concrete route defined component was rendered?

Related

How to solve useEffect running twice? [duplicate]

Consider the snippets below. With React 18, count gets printed twice to the console on every render but with React 17 it gets printed only once.
React 18 Example:
function App() {
const [count, setCount] = React.useState(0);
console.log(count);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="root"></div>
React 17 Example
function App() {
const [count, setCount] = React.useState(0);
console.log(count);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
I know this has something to do with StrictMode but I'm not sure what. And also I've always been unclear about how strict mode works and what's its purpose, so I'd appreciate if anyone could highlight that as well.
TL;DR
When components are wrapped in StrictMode, React runs certain functions twice in order to help developers catch mistakes in their code.
And this happens both in React 18 and React 17 but the reason you aren't experiencing this with the latter is because in React 17, React automatically silences logs in the second call.
If you extract out console.log and use the extracted alias to log, then you would get similar behavior with both versions.
const log = console.log;
function App() {
const [count, setCount] = React.useState(0);
log(count);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
Note:
In React 17, React automatically modifies the console methods like console.log() to silence the logs in the second call to lifecycle functions. However, it may cause undesired behavior in certain cases where a workaround can be used.
Starting from React 18, React does not suppress any logs. However, if you have React DevTools installed, the logs from the second call will appear slightly dimmed. React DevTools also offers a setting (off by default) to suppress them completely.
Source
Now let's dive deep to understand what actually happens in strict mode and how it can helpful.
Strict Mode
Strict Mode is a tool that helps identify coding patterns that may cause problems when working with React, like impure renders.
In Strict Mode in development, React runs the following functions twice:
Functional Components
Initializers
Updaters
And this is because your components, initializers & updaters need to be pure functions but if they aren’t then double-invoking them might help surface this mistake. And if they are pure, then the logic in your code is not affected in any manner.
Note: React uses the result of only one of the calls, and ignores the result of the other.
In the example below observe that components, initializers & updaters all run twice during development when wrapped in StrictMode (snippet uses the development build of React).
// Extracting console.log in a variable because we're using React 17
const log = console.log;
function App() {
const [count, setCount] = React.useState(() => {
log("Initializers run twice");
return 0;
});
log("Components run twice");
const handleClick = () => {
log("Event handlers don’t need to be pure, so they run only once");
setCount((count) => {
log("Updaters run twice");
return count + 1;
});
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
Few notes from the above example:
You might have noticed that when you click the button for the first time the Updaters run twice log prints only once but on subsequent clicks it prints twice. But you can ignore this behavior and assume that it always prints twice but if you want more details about the same you can follow this github issue.
We had to extract console.log into a separate variable to get logs for both the invocations printed and this is because React 17 automatically silences logs for the second call (as mentioned in the TL;DR). If you update the CDN link to React 18, then this extraction wouldn't be required.
Calling the setCount updater function twice doesn’t mean that it would now increment the count twice on every click, no, because it calls the updater with the same state both the times. So, as long as your updaters are pure functions, your application wouldn’t get affected by the no. of times it’s called.
"Updaters" & "Initializers" are generic terms in React. State updaters & state initializers are just one amongst many. Other updaters are "callbacks" passed to useMemo and "reducers". Another initializers is useReducer initializer etc. And all of these should be pure functions so strict mode double invokes all of them. Checkout this example:
const logger = console.log;
const countReducer = (count, incrementor) => {
logger("Updaters [reducers] run twice");
return count + incrementor;
};
function App() {
const [count, incrementCount] = React.useReducer(
countReducer,
0,
(initCount) => {
logger("Initializers run twice");
return initCount;
}
);
const doubleCount = React.useMemo(() => {
logger("Updaters [useMemo callbacks] run twice");
return count * 2;
}, [count]);
return (
<div>
<p>Double count: {doubleCount}</p>
<button onClick={() => incrementCount(1)}>Increment</button>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="root"></div>
How is Strict Mode helpful?
Let's look at an example where Strict Mode would help us find a serious mistake.
// This example is in React 18 to highlight the fact that
// the double invocation behavior is similar in both React 17 & 18.
function App() {
const [todos, setTodos] = React.useState([
{ id: 1, text: "Learn JavaScript", isComplete: true },
{ id: 2, text: "Learn React", isComplete: false }
]);
const handleTodoCompletion = (todoId) => {
setTodos((todos) => {
console.log(JSON.stringify(todos));
return todos.map((todo) => {
if (todo.id === todoId) {
todo.isComplete = !todo.isComplete; // Mutation here
}
return todo;
});
});
};
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>
<span
style={{
textDecoration: todo.isComplete ? "line-through" : "none"
}}
>
{todo.text}
</span>
<button onClick={() => handleTodoCompletion(todo.id)}>
Mark {todo.isComplete ? "Incomplete" : "Complete"}
</button>
</li>
))}
</ul>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="root"></div>
What's the problem with the above example?
You would've noticed that the buttons don't work as expected, they don't toggle the isComplete boolean and the problem is that the updater function passed to setTodos is not a pure function as it mutates an object in the todos state. And since the updater is called twice, and it is not a pure function, the second call reverses the isComplete boolean back to it’s original value.
Note: It's only because of strict mode's double invocation that we were able to catch this mistake. If we opt out of strict mode, then the component would luckily work as expected but that doesn't mean the code is authored correctly, it only works because of how isolated the component is and in real world scenarios mutations like these can cause serious issues. And even if you luckily get away with such mutations, you might still encounter problems because currently the updater relies on the fact that it's only called once for every click but this is not something that React guarantees (with concurrency features in mind).
If you make the updater a pure function, it would solve the issue:
setTodos((todos) => {
logger(JSON.stringify(todos, null, 2));
return todos.map((todo) =>
todo.id === todoId ? { ...todo, isComplete: !todo.isComplete } : todo
);
});
What's new with Strict Mode in React 18
In React 18, StrictMode gets an additional behavior to ensure it's compatible with reusable state. When Strict Mode is enabled, React intentionally double-invokes effects (mount -> unmount -> mount) for newly mounted components. This is to ensure that a component is resilient to being "mounted" and "unmounted" more than once. Like other strict mode behaviors, React only does this for development builds.
Consider the example below (Source):
function App(props) {
React.useEffect(() => {
console.log("Effect setup code runs");
return () => {
console.log("Effect cleanup code runs");
};
}, []);
React.useLayoutEffect(() => {
console.log("Layout effect setup code runs");
return () => {
console.log("Layout effect cleanup code runs");
};
}, []);
console.log("React renders the component")
return <h1>Strict Effects In React 18</h1>;
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="root"></div>
The App component above declares some effects to be run on mount and unmount. Prior to React 18, the setup functions would only run once (after the component is initially mounted) and the cleanup functions would also run only once (after the component is unmounted). But in React 18 in StrictMode, the following would happen:
React renders the component (twice, nothing new)
React mounts the component
Layout effect setup code runs
Effect setup code runs
React simulates the component being hidden or unmounted
Layout effect cleanup code runs
Effect cleanup code runs
React simulates the component being shown again or remounted
Layout effect setup code runs
Effect setup code runs
Suggested Readings
ReactWG: How to support Reusable State in Effects
ReactWG: Adding Reusable State to StrictMode
React Docs: Strict Mode
Beta React Docs: My initializer or updater function runs twice
Beta React Docs: My reducer or initializer function runs twice
React 17 Strict Mode was also double rendering but it was suppressing the logs. That is why we were not seeing double logs. From react 17 docs:
Starting with React 17, React automatically modifies the console
methods like console.log() to silence the logs in the second call to
lifecycle functions. However, it may cause undesired behavior in
certain cases where a workaround can be used.
The difference of React 18 is it is now showing the double renderings. If you are using "React Developer Tools" chrome extension with, you can see that which logs are coming from strict mode:
One of the major additions to React 18 is Concurrency so Strict Mode will also help us see concurrency-related bugs during development.

How do I set up dynamic imports correctly (for beyond localhost)?

I followed https://docs.meteor.com/packages/dynamic-import.html to set up dynamic imports, and it works fine on localhost.
For context, I am creating a blog (Meteor/React/Apollo) which renders MDX files, and these files need to be imported, so I have a list of all my posts as such:
import("./imports/posts/61a000d03a1931b8819dc17e.mdx")
import("./imports/posts/619cae2f03f4ff710aa3d980.mdx")
import("./imports/posts/619e002d386ebf2023ea85c3.mdx")
import("./imports/posts/619fff7c5b312d7622acda86.mdx")
I have a Post.jsx component:
import React, { useState, useRef } from "react"
import { useHistory, useParams } from "react-router-dom"
import { useQuery } from "#apollo/client"
import { GET_POST_ID } from "../../api/posts/queries"
const Post = () => {
const Post = useRef()
const history = useHistory()
const { slug } = useParams()
const [loadedPost, setLoaded] = useState(false)
const [viewer, showViewer] = useState(false)
const open = () => showViewer(true)
const { data, loading, error } = useQuery(GET_POST_ID, { variables: { slug }})
if (loading) return null
if (error) {
console.log(error)
return null
}
import(`./posts/${data._id}.mdx`).then(MDX => {
Post.current = MDX.default
setLoaded(true)
}, (err) => {
console.log(err)
})
return loadedPost ? (
<>
<div className="postContent">
<div className="markdownOverride markdown-body">
<Post.current />
</div>
</div>
</>
) : null
}
export default Post
This works well and good on my local network. However, if I attempt to access it from outside my local network, an error is thrown in the console that all the blog modules are not found. The Apollo/GraphQL portion works fine, but the actual module can't be imported.
How do I get this to work outside of localhost?
Thanks.
EDIT: The error messages are, for each post:
Uncaught (in promise) Error: Cannot find module '/imports/posts/61a000d03a1931b8819dc17e.mdx`
And when I load the actual post page:
Uncaught (in promise) TypeError: Failed to fetch
Isn't your error thrown by console.log(err) ?
import(`./posts/${data._id}.mdx`).then(MDX => {
Post.current = MDX.default
setLoaded(true)
}, (err) => {
console.log(err) // <---- here
})
This means your path isn't right for /imports/posts/61a000d03a1931b8819dc17e.mdx.
To me you can't use changing parameters when doing dynamic imports.
./posts/${data._id}.mdx, because your meteor or webpack compilation needs to treat all the data._ids avalaible in your database in order to compile and prepare the file...
This might be why it works in development mode but not in production.
You can just do dynamic imports of modules or components (already compiled), no more to me. Take a look at your output compilation bundles, and try to find where are your components...
It turns out that I needed to specify the ROOT_URL correctly when initializing Meteor. With an ngrok http tunnel on port 3000 pointing to https://some-hash.ngrok.io, I had to start Meteor with: ROOT_URL="https://some-hash.ngrok.io" meteor. When I do this, I can access it fine and everything loads from my local IP and the ngrok URL, but I can't seem to load it up from localhost (it times out).
Specifying my local or public IP did not work, I could not get it to load through any of those methods.

Next.js router error while testing using React-testing-library

I was just trying to do a preliminary test of rendering a component that is redirecting the user on the basis of the login status and thus using router from next/router in componentDidMount but getting the following error:
No router instance found.
You should only use "next/router" inside the client side of your app.
It appears to me that from client side it means using the router or Link has to be used inside of the render method as that is what makes the DOM and other methods like lifecycle, hooks, and server-side doesn't so in those cases it would throw an error.
I know that testing the entire component beats the purpose of unit testing but I wanted to do this anyway. Therefore, I followed this discussion and it appears that the router has to be mocked in order to be used by the React-Testing-Library but none of the solutions work for me.
Here is the code that I tried:
describe('Home Page', () => {
it('renders without crashing', async () => {
render(<Home />)
})
})
a solution like:
import { useRouter } from 'next/router'
...
jest.mock('next/router', () => ({
useRouter: jest.fn(),
}))
...
;(useRouter as jest.Mock).mockImplementation(() => ({
pathname: '/',
push: mockPush,
}))
will work.
the error you're encountering will be triggered if you do not mock useRouter in the initial mock (i.e.:
...
jest.mock('next/router')
...
Mocked the router using the next-router-mock library. Just pasted jest.mock('next/router', () => require('next-router-mock')); before the test and the tests are passing.
jest.mock('next/router', () => require('next-router-mock'));
describe('Home Page', () => {
it('renders without crashing', async () => {
render(<Home />)
})
})
Although, the test is passing but getting this warning that is not making any sense to me.
› 1 snapshot obsolete.
• Home Page renders without crashing 1
Snapshot Summary
› 1 snapshot obsolete from 1 test suite. To remove it, press `u`.
↳ tests/homePage.test.js
• Home Page renders without crashing 1
Assuming that there is a better way to mock the router.

How to use React.lazy in Gatsby

As using React.lazy in Gatsby you'll get error when making production( gatsby build), what is the best way to use React.lazy and suspense in Gatsby Project
React.lazy and Suspense are still not ready for server-side rendering, but they can still be used by checking that the code is executed only on the client. While this solution is inferior to loadable-components, that works both on server side and client, it still provides an alternative for dealing with client-side only packages, without an added dependency. Remember that the following code could break if executed without the isSSR guard.
import React from "react"
const ClientSideOnlyLazy = React.lazy(() =>
import("../components/ClientSideOnly")
)
const MyPage = () => {
const isSSR = typeof window === "undefined"
return (
<>
{!isSSR && (
<React.Suspense fallback={<div />}>
<ClientSideOnlyLazy />
</React.Suspense>
)}
</>
)
}
Try following this guide on gatsbyjs.com, if the above doesn't work for you

Component should be written as a pure function

I have a react-native android application. My component in index.android.js is stateless so eslint is throwing the error "Component should be written as a pure function". If I make the component as pure function, how do i register the application or how should the instantiation be?
You can register the application even with a "pure function"
This kind of code would work
const App = () => {
return (
<MainApp />
);
};
AppRegistry.registerComponent('myapp', () => App);
The "return" part can be deleted for a cleaner code:
const App = () => (
<MainApp />
);
AppRegistry.registerComponent('myapp', () => App);

Resources