How to test how many times a component was rendered in React? - javascript

I have a scenario with 2 components:
App
People
I want to test if People gets rendered 10 times inside App. So, I'm trying to test that using Jest. So far, I did this on my src/App.test.js:
import React, { Component } from "react";
import People from "./components/People";
import App from './App';
test('Total people = 10', () => {
expect(App).find(People).toHaveLength(10);
});
But I get a message saying:
TypeError: expect(...).find is not a function.
How can I test how many times a component gets rendered inside another component using React and Jest? Can anyone help me?

For testing react components first you need to render them, there are some tools for doing that, but since your reasoning in this test is to check how many times a component has been rendered inside another component, enzyme does a good job with its shallow method.
import React from "react";
import App from "./App";
import People from "./components/People";
import { shallow } from "enzyme";
it("Total people = 10", () => {
const wrapper = shallow(<App />);
expect(wrapper.find(People)).toHaveLength(10);
});
You'll need to set up enzyme in your project first, read the docs for more details.
The current trend in testing is to check for the things the user actually sees in the page, so most people is using react-testing-library, It'll be good for you to check it out

If you ever switch to react-testing-library, you might write the test something like this:
import React, { Component } from "react";
import App from './App';
import {render} from "#testing-library/react";
test('Total people = 10', async () => {
const { getAllByText } = await render(<App />);
expect(getAllByText('string_in_People')).toHaveLength(10);
});
Basically you'd use one of the library's built-in getAllBy... query methods to query for all instances of an element that appears exactly once in each instance of your <People /> component. The resulting set's length will equal the number of <People /> instances on the page.

Related

Component Palette Custom Hook

I am fairly new to React and still wrapping my head around custom-hooks. I cam across a code where a custom hook was created to handle the component imports.
useComponentPalette.js
import {TodoEditor} from './components/TodoEditor'
import {TodoItem} from './components/TodoItem'
import {TodoList} from './components/TodoList'
import {CheckBox} from './components/CheckBox'
const defaultComponents = {
TodoEditor,
TodoItem,
TodoList,
CheckBox
}
export function useComponentPalette(){
return defaultComponents
}
And then in order to use the hook,
const {TodoItem, TodoList, Checkbox } = useComponentPalette()
My Question :- Does this approach provides any advantage over the regular imports in the component ? or this is an anti-pattern ?
How I usually import the components is as follows
import {TodoEditor} from './components/TodoEditor'
import {TodoItem} from './components/TodoItem'
import {TodoList} from './components/TodoList'
import {CheckBox} from './components/CheckBox'
function App(){
return(
<>
<TodoList/>
</>
)
}
It's not a good idea to use react hooks like this you can get the same result without react hook
// first file name.js
import {TodoEditor} from './components/TodoEditor'
import {TodoItem} from './components/TodoItem'
import {TodoList} from './components/TodoList'
import {CheckBox} from './components/CheckBox'
export default {
TodoEditor,
TodoItem,
TodoList,
CheckBox
}
//component file
import * as Component form 'first file name';
//<Component.TodoEditor/>
//or
import {TodoEditor} form 'first file name';
The way that I use react-hooks is for making my code more dry and increase it's readability, so react-hooks is not good fit for this kind of usage.
Hi #Sachin,
In my option, React JS use hook to manage reuse stateful logic between components. In other word, Hooks do well to encapsulating state and share logic. If you want to do some stateful logic or condition base logic with these components, then it's fine with that. But if you are using just without condition in the given components. Then, This Is useless for making the custom hook. You can do that without a custom hook in a simpler way.
Here is a simple way to do that:-
In components folder. I create index file, this is the entry point of all my exporting components
In that file. I export all my components, as you can see.
I use that components like this. It much better way. In my option.
import { Header, Footer, Sider } from "./components"
before using react custom hooks, we should be aware of the rationale behind it.
Customs hooks functionality was provided to reuse stateful logic. If logic doesn't require any state, we will use simple functions and if it is about components only there there are different patterns for making code general and scaleable.
So, there is no usage of custom hook in above case at all. For me, I would go with the following code for above scenario:
// components/index.tsx
import {Todo} from './todo'
import {CheckBox} from './components/CheckBox'
export {
Todo,
CheckBox
}
// componentns/todo/index.tsx
import {Editor} from './Editor'
import {Item} from './Item'
import {List} from './List'
const Todo = {
Editor,
Item,
List
}
export default Todo;
and usage will be like
import { Checkbox, Todo } from "components"
...
<Checkbox ... />
<Todo.List ...>
<Todo.Item ... >
</Todo.Editor ... />
</Todo.Item ... >
</Todo.List>
...
P.S Usage can be different based upon the logic of components, just giving an hint how we can patterns to serve our purpose.
Hope it helps.

Invalid Hook Call use-neo4j

I recently started building a react app.
I wanted to configure a neo4j database attached to the application. I decided to use the use-neo4j hook. I followed the basic step of creating the driver instance like this:
import React from "react";
import ReactDOM from "react-dom";
import "./css/index.css";
import App from "./js/App";
import Nav from "./js/Nav";
import { Neo4jProvider, createDriver } from "use-neo4j";
const driver = createDriver("neo4j", "localhost", 7687, "lode", "neo4j");
ReactDOM.render(
<React.StrictMode>
<Neo4jProvider driver={driver}>
<Nav />
<App />
</Neo4jProvider>
</React.StrictMode>,
document.getElementById("root")
);
But I ended up having the Invalid Hook Call error.
Except if I remove the Neo4jProvider tags
Seen in this image here
I tried everything I can to fix it, I'm pretty new at React.
I would love someone to help me if they can.
Just like the error says, you can only call hooks inside the body of a functional component. So I would probably create a component that initiates the driver and returns the component that uses it -
import { Neo4jProvider, createDriver } from "use-neo4j";
const NeoProvider = ({ children }) => {
const driver = createDriver("neo4j", "localhost", 7687, "lode", "neo4j");
return (
<Neo4jProvider driver={driver}>
{children}
</Neo4jProvider>
);
}
And then import that at your top level and use that instead of the Neo4jProvider you're importing from the package.
Fixed the issue, I had multiple Reactjs running

good practice to import asterisk for package/lib?

I've seen somewhere in github people import *, I have no clue why he did so, for instance:
import * as React from 'react';
export const doSomething = () => {
return React.useMemo(() => {
return {
something: window.location.href
};
}, []);
};
He did the same for other packages. Why don't we do
import { useMemo } from 'react' ?
Both of the suggestions for using * or explicit imports are possible and will work as expected.
The asterix will import all components, hooks, and more artifacts that are located under the "react" library, so when using
import * from "react";
you'll have the freedom to use any type of hook or Component without an explicit import.
The best practice though is to import only modules that are relevant to your application or components
import React, { useMemo } from "react";
This approach will make sure you don't import unused components that will be heavy on your bundle size for production and will be flagged as dead code.
Ok, to expand my answer I will say: You have two options
import React, { useMemo } from 'react';
Benefits? You are just importing the things you need, also, your code will be a little bit shorter compared to the second option since you can use useMemo(); directly
Also this is the way reactjs.org presents their examples https://reactjs.org/docs/hooks-intro.html which is a big -GOOD PRACTICE- signal
import React from 'react';
it's seem similar to the first option but you are using React directly.
how to access useMemo()? React.useMemo(). So yes, you guessed, it's a little bit longer sintax that the first option, but i do not think this is a problem.
Also this is the way https://material-ui.com/components/text-fields/ material ui presents their examples. It's also a nice way to do it (and the one I use)

How to traverse a shallow component nested in ThemeProvider HOC?

Following up the issue on Github, I have a component Comp that when exported, is wrapped with injectSheet from reactjss. Please see the setup on codesandbox.
In a unit test, I'd like to assert that that component contains <a>, which it does (see codesandbox), but the test fails regardless:
describe("<Comp /> component", () => {
const wrapper = shallow(<Comp />);
it("should render a <a>", () => {
expect(wrapper.find('a')).to.have.length(1);
});
});
I get Error: [undefined] Please use ThemeProvider to be able to use WithTheme. So my natural (perhaps not the correct?) reaction was to wrap the component with ThemeProvider:
const wrapper = shallow(
<ThemeProvider theme={{}}>
<Comp />
</ThemeProvider>
)
Then I get AssertionError: expected { length: 0 } to have a length of 1 but got 0.
I tried a whole slew of approaches, including calling dive, find or first with an extra shallow call, but I would always end up with Please use ThemeProvider to be able to use WithTheme:
// 1. dive(), as suggested in
// https://github.com/cssinjs/react-jss/issues/30#issuecomment-268373765
expect(wrapper.dive('Comp')).to.have.length(1);
expect(wrapper.dive('Comp').find('a')).to.have.length(1);
expect(wrapper.dive().find('a')).to.have.length(1);
// 2. find() / first(), as suggested in https://github.com/airbnb/enzyme/issues/539
expect(wrapper.find(Comp).shallow().find('a')).to.have.length(1);
expect(wrapper.first().shallow().find('a')).to.have.length(1);
Any ideas here? I am a bit new to unit testing with React, so I would appreciate if someone could enlighten me on this ;)
For anyone still struggling with this, one viable approach was suggested on GitHub. Instead of testing the styled component wrapped with injectSheet HOC, you export your stand-alone component and test it in isolation
// Component.js
import React from 'react'
import injectSheet from 'react-jss'
const styles = {
color: 'burlywood'
}
// named export for unit tests
export const Component = props => <h1>Component</h1>
// default export to be used in other components
export default injectSheet(styles)(Component)
which would work for most use cases, since more often than not, you need to unit test the plain component and its logic, and not any of its associated styling. So in your unit test just do
import { Component } from './Component'
instead of (which you would do in the rest of your codebase)
import Component from './Component'

Redux on functional components in Reactjs (web)

I am writing this question cause I would like to ask you for some help in how to use the redux on my functional components. I had a look at other examples with React components but I cannot understand how to get the "store" value in functional components.
My idea is to use my
store.getState()
To check states and interact with the UI, inside my functional component but I cannot make it happen.
Any help please ?
For example, a functional component :
import React from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from './Header.css';
import { Navbar, Nav } from 'react-bootstrap';
import HeaderMenu from '../HeaderMenu';
import cx from 'classnames';
function Header() {
return (
<Navbar fluid fixedTop id="Header" className={s.navContainer}>
<Nav block className={cx(s.HeaderTitle, s.hideOnSmall)}>Project title</Nav>
<HeaderMenu />
</Navbar>
);
}
export default withStyles(s)(Header);
How can I use the "store" object inside my Header component ? It works on my App component, just I don't know how to use it within my components.
My questions are:
Should I use actions for retrieving the state instead ??
Should I pass the store object component to the component properties?
Thanks in advance!
EDIT :
I am using https://github.com/kriasoft/react-starter-kit
with the redux branch https://github.com/kriasoft/react-starter-kit/tree/feature/redux
As of version 7.x react-redux now has hooks for functional components.
Header.jsx
import React from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from './Header.css';
import { Navbar, Nav } from 'react-bootstrap';
import HeaderMenu from '../HeaderMenu';
import cx from 'classnames';
import { useSelector } from 'react-redux'
function Header() {
const store = useSelector(store => store)
return (
<Navbar fluid fixedTop id="Header" className={s.navContainer}>
<Nav block className={cx(s.HeaderTitle, s.hideOnSmall)}>Project title</Nav>
<HeaderMenu />
</Navbar>
);
}
export default withStyles(s)(Header);
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
As Dan Abramov mentioned in his insanely famous Egghead.io series, presentational component shouldn't be aware of Redux store and shouldn't use it directly. You should create something called container component, which will pass necessary state fields + action callbacks to your presentational component via properties.
I highly recommend to watch Dan Abramov's Egghead.io series if above concepts are not familiar to you. Patterns he is describing there are de facto standard style guide for writing React+Redux applications nowadays.
If you want to get the Redux state on a functional component, you either have to manually connect the component and Redux, which is very tedious. You would have to access the reducer variable inside your React Component, meaning import the reducer into the component file among other configurations.
This would interfere with other benefits you would normally get, such as action creators, dispatching actions automatically through middleware, and more.
A cleaner idea is to just use the Provider Component that comes with React-Redux to turn your Header component into a Container. A Container is a react component that 'listens for' any changes that have been made to the store. You are essentially wrapping your Header component in a higher order component that is connected directly to the redux store.
This way is more scalable and you can easily create a boilerplate file that can be used with any React/Redux project.
Try to understand the individual modules/components a boilerplate project contains before going with it. If you are new to react (and its complementary libraries) I recommend you start here:
https://github.com/petehunt/react-howto
For redux:
http://redux.js.org/
These are both great ressources which clarify the majority of react related questions on stackoverflow.
You can use react-redux library, and using connect you will access your store data as a component props - it's easy and efficient.

Categories