How to test function in a react SFC which using hooks? - javascript

https://reactjs.org/docs/hooks-faq.html#how-to-test-components-that-use-hooks
the document show that we should use React DOM to test component.But in many cases, our logical components render another ui component.And maybe the function in logical component will be passed to the ui component as a prop, just like
function Foo(){
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
function onChange(value) {
setCount(value);
}
return <Bar value={count} onChange={onChange} />
}
function Bar(props){
const { value, onChange } = props;
return (
<div>
<p>You clicked {value} times</p>
<button onClick={() => onChange(count + 1)}>
Click me
</button>
</div>
);
}
In this case, how can I test the onChange() and other functions which can't be obtained in DOM?This is the simplest case. If we use some component library like material-design and ant-design, we usually don't know the DOM structure.

As the docs suggest:
If your testing solution doesn’t rely on React internals, testing
components with Hooks shouldn’t be different from how you normally
test components.
The goal is not to test the onChange function at all, whether the hooks work or not is already tested by the React team.
Your current change to render a component does not change the test at all, rendering Foo still renders a button and p however deep the component chain goes.
On using a framework like Antd or Material Design, it might be difficult to know the complete DOM structure. But instead, or better you can query by things that the user sees on your page.
For eg. using the React testing library recommended in the docs:
const button = getByText(container, /Click Me/i);
This ties in directly with what user is seeing on the page and leads to much better tests.

Related

Handling a modal visibility with parent state vs handing it with imperativeHandle and modal state

I need to implement a reusable modal. I have basically two implementations in my mind.
Handle the modal visibility in the parent state. Then I'll have to pass the state setter into the modal component as I'll always have a button inside the modal to close the modal.
const Modal = (props) => {
return (
<div>
<button onClick={() => props.setVisibility(false)}>Cancel</button>
</div>
);
};
export default function App() {
const [showModal, setShowModal] = useState(false);
return (
<div className="App">
<button onClick={() => setShowModal((p) => !p)}>Toggle Modal</button>
{showModal && <Modal setShowModal={setShowModal} />}
</div>
);
}
Handle the modal visibility in the modal component itself, but expose a function to show/hide the modal using an imperativeHandle.
const Modal = React.forwardRef((props, ref) => {
const [showModal, setShowModal] = useState(false);
useImperativeHandle(ref, () => ({
hide: () => setShowModal(false),
show: () => setShowModal(true),
toggle: () => setShowModal((p) => !p)
}));
if (!showModal) return null;
return (
<div>
<button onClick={() => setShowModal(false)}>Cancel</button>
</div>
);
});
export default function App() {
const modal = useRef();
return (
<div className="App">
<button onClick={() => modal.current.toggle()}>Toggle Modal</button>
<Modal ref={modal} />
</div>
);
}
I prefer the second one, as I think it looks cleaner, without a bunch of states polluting the parent component(there will be multiple modals on one page). The toggle function is only there for the example, I'll only ever need the show and hide functions. Plus, the visibility of the modal is a property of the modal, and I feel like the modal should be the component that is handling it. But I've heard people and even the react docs saying
imperative code using refs should be avoided in most cases
So, even though I think this is an acceptable use of refs, that's what's holding me back. I want to know,
which of the two is the react way of doing this
are there any differences in performance between the two ways
are there possibilities of bugs occurring if I decide to use refs
Regarding you 3 questions:
The react way is definitively the way without useImperativeHandle. React code should be declarative, not imperative.
I don't know about performance. I would not expect a huge difference.
The useImperativeHandle will technically work fine, there will be no immediate bugs. But the idea behind the react way is to keep the code maintainable, easy to understand, and avoid future bugs and confusion.
Examples for problems with the imperative way:
Imagine you suddenly need to open / close a modal from multiple components. If the state is in the parent,
this change is simple, you can easily pass the state and the setState callback where ever you want.
If you use useImperativeHandle you need to make sure the state is consistent, which in essence means
handling the state in some parent component anyway.
Or imagine you decide to (or need to) refactor to use a reducer pattern, and dispatch a openModal action instead of
calling the show "method" of your component. Again, that is easy if you are only handling states and callbacks anyway.
How to follow the declarative way, and why:
I totally understand your desire to keep the state inside the modal. I feel the same (and would be happy to be corrected here).
But I'm also happy time and again that I didn't couple some state with other code, when I need to make "a quick change".
If you have multiple states and don't want to clutter your main component, you can use a custom hook, as you probably know.
And that's the beauty of this declarative style:
You don't have to think too much about that custom hook for now,
you can just move the states in there as they are.
If you later feel that you should rename something or organize the state differently
(but still following the react way), it is easy to throw states around between components
(or hooks, or redux, ...),
always following the same patterns,
thinking only about what you are doing right now, moving states, without thinking of the logic they are related to,
without getting confused too much.

How to test a function passed as props to child in React?

I need some advice on how to write tests for React component. I have 2 approaches in my mind, feel free to suggest more.
I have a component App which renders a ComplexSearchBox. I pass this ComplexSearchBox a prop onSearch, the value of this prop is a function. This function is invoked by ComplexSearchBox at various user interactions like when the user hits enter on the input box or clicks the search button.
const App = () => {
return <ComplexSearchBox onSearch={query => console.log(query)}/>;
};
I want to write tests for App component. I'm using Enzyme to write tests.
I'm following some principles of React Testing Library.
I'm mounting the component. So this will render all children as well. I don't want to render ComplexSearchBox so I'll mock it.
This is where my problem is. I have 2 approaches in my mind.
I can get the props of ComplexSearchBox and invoke the method directly with the required parameters.
jest.mock('Path To ComplexSearchBox', function ComplexSearchBox() {
return null;
});
describe('App', () => {
describe('when search button is clicked', () => {
it('should log to console by invoking prop method', () => {
const wrapper = mount(<App/>);
wrapper.find('ComplexSearchBox').props().onSearch('My random test query');
//expect something
});
});
});
I can mock the ComplexSearchBox and return a simplified version of it. Now I can type the query in an input box and then click a button to submit.
jest.mock('Path To ComplexSearchBox', function ComplexSearchBox({onSearch}) {
const [query, setQuery] = useState('Sample Search Query');
return <div>
<input value={query} onChange={setQuery}/>
<button onClick={() => onSearch(query)}/>
</div>;
});
describe('App', () => {
describe('when search button is clicked', () => {
it('should log to console by clicking', () => {
const wrapper = mount(<App/>);
wrapper.find('input').simulate('change', {target: {value: 'My random test query'}});
wrapper.find('button').simulate('click');
//expect something
});
});
});
I see value in the second approach. However, I'm not sure if it is worth the effort of creating a simplified version every time I have to interact with a child component.
The benefit of the second approach is that
It decouples the code from tests. My tests don't have to know which method to invoke with what parameter when the user wants to execute a search. Mock knows which method to invoke but that is at one place and not spread across all the tests.
I find tests to be more readable and behaviour oriented when written this way.
This mock can be extracted out and used at multiple places. Making writing tests easier.
Any method sequencing can be abstracted in the mock component. Like if I modify the ComplexSearchBox as below.
const App = () => {
return <ComplexSearchBox preProcess={()=>{}} onSearch={query => console.log(query)} postProcess={()=>{}}/>;
};
jest.mock('Path To ComplexSearchBox', function ComplexSearchBox({preProcess, onSearch, postProcess}) {
const [query, setQuery] = useState('Sample Search Query');
const search = (query) => {
const preProcessedQuery = preProcess(query);
const searchedQuery = onSearch(preProcessedQuery);
postProcess(searchedQuery);
};
return <div>
<input value={query} onChange={setQuery}/>
<button onClick={() => search(query)}/>
</div>;
});
Though I'm not very sure if the last benefit is really a benefit. As now my mock is aware of the lifecycle of ComplexSearchBox. But on the other hand, this mock will be written only once and will save me from calling those 3 methods one after the other in a lot of tests.
I could also argue that a component test written with approach one should not really care about the method sequencing as that is ComplexSearchBox responsibility.
Those 3 methods do have a tight coupling as one's output is next one's input. And now I'm borderline integration testing these 2 components.
I could also have 3 buttons which have onClick to run those 3 methods and now I can test them individually.
I'm not really sure which approach is better. I'm leaning a bit towards approach 2 because it makes my tests less dependent on implementation.
I'd appreciate any advice on this and if you have another way to test this scenario then please share.
I don't have an exact answer for you, but I highly suggest reading this article:
https://kentcdodds.com/blog/why-i-never-use-shallow-rendering
In it, you will find arguments against shallow rendering, and in favour or integration tests in React.
The points you made are all valid, if you choose the route of unit testing, therefore mocking all subcomponents of the <App /> component, you will be coupling your tests to the current implementation of those subcomponents, and anytime you change those subcomponents, even if the refactor doesn't change the behaviour, you run the risk of breaking the <App />s test.
I don't believe there is a way around it when you are dealing with mocks, so I would suggest considering the article above and not mocking anything, which might run the tests a little slower, but it probably won't be a big deal most of the time!

React - unnecessary rendering

I am learning ReactJS. I would like to use functional component. In my component, I have a textbox and a clear button.
My concern is - whenever I type a char from my keyboard to enter info for the text box, I see the search account called!!! in my console output. If i type 5 chars, I would see this 5 times - it basically means the whole component is re-rendered & methods re-defined.rt?
Is it not bad practice and affect the performance? Is there any other alternative?
import React, { useState, useContext } from 'react';
import AccountContext from . './accountContext'
const SearchAccounts = () => {
const [text, setText] = useState('');
const onChange = (evt) => setText(evt.target.value);
console.log('search account called!!!');
// some methods are defined here
onSubmit = () => {...}
onClear = () => {...}
return (
<div>
<form onSubmit={onSubmit} className="form">
<input
type="text"
name="text"
value={text}
onChange={onChange}
placeholder="Search Accounts..."
/>
<input type="submit" value="Search" className="...." />
</form>
<button
className="...."
onClick={onClear}
style={getClearStyle()}
>Clear</button>
</div>
);
}
export default SearchAccounts;
Re-renders aren't necessarily expensive, and you have to accept that your components will re-render frequently in order propagate the changes in your data to the UI. Your example is very cheap, since the component is small and does not render any additional components in its return function - this is the ideal way to compose React components that have to re-render often.
You have to remember also that your JSX is not trashing and appending all HTML elements to the DOM every time the component re-renders. Only the difference between the last render and the current one is being applied, which is what allows React and other front end frameworks to create smooth and fast UIs when built at scale.
If and when you do reach a bottleneck in your components, you can use memoisation techniques (React.memo in a functional component context, shouldComponentUpdate in a class context) to prevent regularly rendering components from affecting the performance of their component children. It's usually best to do this towards the end of a project or unit of code, and only as a final measure since memoisation escapes built in React optimisation and can actually cause more problems than it solves if you use it inappropriately. A well structured component tree and flux-based state solution will alleviate most performance issues.
My concern is - whenever I type a char from my keyboard to enter info for the text box, I see the search account called!!! in my console output. If i type 5 chars, I would see this 5 times - it basically means the whole component is re-rendered & methods re-defined.rt?
Yes, this is expected and aligns with the mental model of React.
In React, you'll hear people say "the view is a function of state" — meaning that whatever data you inside of variables in react state should directly determine the rendered output, sometimes refered to as "one-way data flow"
The consequence of doing this however is that, in order to determine what changed, you need to render the react component.
This is fine!
Remember what powers React — the virtual dom. Internally, react creates a light-weight representation of the browser's DOM so that the above one-way data flow is efficient.
I see a separate concern here though regarding the amount of time you see the console.log output.
What you're doing there is a "side-effect" in React, and whenever you have a side-effect, you should wrap in in useEffect. useEffect will ensure your side-effect only runs when the values you feed to its dependency array changes.
const { useEffect, useState } = React;
const { render } = ReactDOM
function MyComponent() {
const [value, setValue] = useState('world');
useEffect(() => {
console.log('this should only render once');
}, []);
useEffect(() => {
console.log(`this will render on every value change: the value: ${value}`);
}, [value])
return (
<label>
hello, <input value={value} onChange={e => setValue(e.target.value)} />
</label>
);
}
render(<MyComponent />, document.querySelector('#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>
See here for more on how useEffect works and here if you want a deep dive.

React: passing mapped data from button onclick to display on another component

I'm having some difficulty passing data that I have mapped from an API, it displays a bunch of Event cards based on how many events there are in the API array.
This my Event Cards component;
export default function EventCard() {
const classes = useStyles();
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
const result = await axios("http://localhost:23455/Event");
setData(result.data);
};
fetchData();
}, []);
const handleClick = (value) => () => {
console.log(value);
};
return (
<div>
<Row>
{" "}
{data.map((item) => (
<Card
className={classes.root}
style={{ marginRight: "25px", marginBottom: "25px" }}
>
<CardHeader
avatar={
<Avatar aria-label="recipe" className={classes.avatar}>
{item.icon}
</Avatar>
}
action={
<IconButton aria-label="settings">
<MoreVertIcon />
</IconButton>
}
title={item.name}
subheader="September 14, 2016"
/>
<CardMedia
className={classes.media}
image={LordsLogo}
title="Paella dish"
/>
<CardContent>
<Typography variant="body2" color="textSecondary" component="p">
<p key={item.id}> {item.description}</p>
<p key={item.id}> {item.startDate}</p>
<p key={item.id}> {item.endDate}</p>
</Typography>
</CardContent>
<CardActions disableSpacing>
<IconButton aria-label="add to favorites">
<Button variant="outlined">Steward</Button>
</IconButton>
<IconButton aria-label="share">
<Button variant="outlined" onClick={handleClick({ item })}>
Tickets
</Button>
</IconButton>
</CardActions>
</Card>
))}
</Row>
</div>
);
}
I have an onclick function which logs what data is being added into "value" onclick, if I click a card, the console log the value of that specific card:
What I'm trying to do now is use this information in another component called ServiceForm. I want to be able to click the button, be linked to ServiceForm, and use the variables within "Item" in the ServiceForm component, Ideally as shown below;
<Form.Group as={Col} controlId="formGridForeName">
<Form.Label>**{item.description}**</Form.Label>
<Form.Control
name="firstName"
placeholder="Enter name"
onChange={this.handleChange}
/>
</Form.Group>
EventCard if a functionalComponent and ServiceForm is a class based component, how could I pass the information from the first to the latter? Thanks
Edit: To show component hierarchy
ServiceForm is rendered in ServiceApplication, as shown:
import * as React from "react";
import { ServiceForm } from "../components/ServiceForm";
class ServiceApplication extends React.Component {
render() {
return (
<React.Fragment>
<h1>Service Application</h1>
<h6>Users can use this page to apply for tickets.</h6>
<ServiceForm />
</React.Fragment>
);
}
}
export default ServiceApplication;
EventCard component is rendered in EventsPage, as shown below;
import EventCard from "../components/EventCards/EventCard";
class EventsPage extends React.Component {
render() {
return (
<React.Fragment>
<h1>Events</h1>
<h6>
Welcome to the Events, here you can apply for seats or tickets at the
events shown below.
</h6>
<Row>
<EventCard />
</Row>
</React.Fragment>
);
}
}
export default EventsPage;
The idea is to pass on the ID when Clicking the 'Tickets' button on the EventCard (the ID is being pulled from an API and mapped).
It's a question of how you want to store state in your app really.
Take a simple example where a component which is a child of another. In that case, the parent can store the state locally e.g. useState() and pass it down as props to the child.
const SomeParent = () => {
const [isTrue, setIsTrue] = React.useState(true)
return (
<Child isTrue={isTrue} />
)
}
There may be cases where you want to share state across multiple components, or across your whole app. In that case, you have a load of different options.
1. Lifting state up to the highest point you need.
It would be easiest to move your state which is shared between multiple components up to the highest point that the two components share.
e.g.
Section component <- store state here
Parent one
child one
child two
Parent two
child one
Here you can again either useState() or alternatively store the state in a useReducer() and pass dispatch down.
The data flow would then look something like this:
initialise state in the section component e.g. const [someState, setSomeState]
pass the setSomeState callback down to the Parent component and in turn child component as a prop e.g.
In the child component set state onClick using the callback e.g. onClick = {() => action(item)}
pass the state value down to your second Parent component and in turn child component e.g.
You have access to the state in your form
This can be an object or multiple state values. As complexity increases, I tend to reach for useReducer().
2. Implementing useContext OR Redux in your application to create an application state accessible from any component (This is a much bigger explanation and there are lots of resources on each...
NOTE: I'm not sure why you're using two sets of brackets here:
const handleClick = (value) => () => {
console.log(value);
};
and when you're calling handleClick you need to switch it for an arrow function if you're passing a value:
onClick={handleClick({ item })
...
onClick={() => handleClick(item)}
Try this:
const handleClick = (value) => {
console.log(value);
};
<Button variant="outlined" onClick={() => handleClick({ item })} />
basically, you want to trigger a class component function from a functional component by passing item prop, if it was a parent child components you can pass a function as a prop and trigger it whenever you want from your parent or child component.
but in your case both components are not connected, what you can do without using redux is to create a public local storage for ex let's call it localStorage.js :
var PublicData = (function() {
var data = null;
var getData = function() {
return data;
};
var setData = function(props) {
data = props;
};
var clearData = function() {
data = null;
};
return {
GET: getData,
SET: setData,
CLEAR: clearData
}
})();
export default PublicData;
then you import it and use like the following :
import PublicData from './PublicData.js';
PublicData.SET(apiData); // you set your data once you fetch it
const data = PublicData.GET(); // get data
PublicData.CLEAR(); // clear your data
Note : this is not an actual localStorage, but it work the same, it will help you to share variables between your components.
Parent
/ \
Service Event
I'm guessing from your question that a simplification of your app tree looks like the above.
The problem you are having is the following:
Parent
/ \
Service Event
Data
In a top down architecture Service does not know about Event and any data over there in the that branch.
So you have 2 options (likely more, but these 2 get you a long way):
Hoist the data
Parent
Data
/ \
Service Event
Store the data somewhere else and provide a mechanism for access
Data
Parent
/ \
Service Event
Option 1 can be achieved by passing props (the data) and functions (passed using props) to manipulate the data (or, indeed, populate it in the first place i.e. fetch it)
The flow would look something like:
Parent is stateful. Parent passes the current state of the data to children that care about it (in your case that looks like Service and Event). Parent passes a function to Event, which can be attached to a click handler within the Event subtree of elements.
Due to passing a function down, when that click handler is invoked it can set the state of the Parent, from which the normal top-down rendering flow will handle setting updates and passing the data changes down the tree, whereby they will (usually) invoke a re-render.
I think Recoil uses this method, and it is becoming increasing popular once more as we look to split applications up and eschew global state management.
Option 2 is where data management libraries like Redux, Cerebral, MobX et al live. If you’re familiar with the publisher/subscriber pattern then you'll know how they work.
In essence, these libraries hold shared state for your application, such as the data you have here. They then (typically) provide a pattern for you to manage changes to the state (via a published event) and ensure that components that subscribe to that data receive the new state and update.
The 'discussion' here differs from option 1:
The click handler publishes an event, which, in essence, asks for some manipulation to the data. Manipulation occurs (or not), and subscribers are updated. In your case, the click would tell the centralised store/s (i.e. anything listening) to fetch the data, when it is done and the data is changed (from empty to filled) it lets all concerned elements (or, quite possibly, the entire application tree and leverage top-down rendering) know about it and they can do what they need to do.
Which option you pick is entirely up to you, they both have pros and cons.
Good luck thinking about your data, how you store it, and how you use it!
I'm glad you asked this question because this is a problem that has been faced and solved a lot of the times. Other contributors have written nice comprehensive answers so I'll keep it short.
1. Move shared stated to a common ancestor.
This one's pretty straight forward but gets very hairy very quickly.
2. Use Context API
You can use react's context API to move code responsible for fetching data to your context and use that data anywhere in your app without a lot of hassle. This approach is getting quite popular nowadays. Take a look at this and here's a sweet library to help you get started.
3. Use a state management library (preferred)
You can choose to use any state management library you like to solve this problem. There are quite a few options, and redux is the most popular one and for a very good reason. It enables you in implementing separation for concern between your data and your UI, but is a bit verbose.
Mobx is also popular and has a good developer experience but I personally don't like that it enforces a certain style to your code.
If you're new to react and state management then these libraries could be really daunting. I'd suggest you start with zustand. It's super simple and gets the job done. ;)
You can simply use Custom Event https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent. It is not the best practice but it work.In EventCard, when user click you dispatch a Custom event with payload is item, then ServiceForm will listener that event and store item as state. Working demo here
https://codesandbox.io/s/distracted-aryabhata-ec6gc
EventCard
const handleClick = () => {
const item = {
description: "This is description"
};
const event = new CustomEvent("customEvent", {
detail: item
});
window.dispatchEvent(event);
};
Service Form
const [item, setItem] = useState(null);
...
useEffect(() => {
const handler = (e) => {
setItem(e.detail);
};
window.addEventListener("customEvent", handler);
return () => window.removeEventListener("customEvent", handler);
}, []);

React: How to use refs in a function component that I have no control over (i.e. from a library)

I can't believe I couldn't find an answer on this so please point me in the right direction if I am wrong.
I am trying to do something simple:
There's a function component from a library that I include.
I need to get a ref to it.
Crucially, React says this:
Ref forwarding lets components opt into exposing any child component’s ref as their own.
Unfortunately, this library component has not opted in using forwardRef.
What is the correct approach to get a ref to this element?
The 3rd party library component in question looks like so (simplified):
function OutboundLink(props) {
return (
<a {...props}
...
/>
)
}
My component looks like so:
function MyComp(props) {
const ref = useRef(null)
return (
<OutboundLink ref={ref} {...props} >
{children}
</OutboundLink>
)
}
but I am greeted with the errors about not being able to pass refs to a function component. Normally I would wrap in forwardRef, but can't in this case since I can't modify the library's code directly.
I did see this and this but didn't think either applied to me.
Any ideas?
You can't directly. There's no hook to let you in if the component wasn't written that way.
I think your best bet would be to wrap the component, capture a ref, and then drill into its children with DOM methods.
const wrapperRef = useRef(null)
const innerRef = useRef(null)
useEffect(() => {
if (wrapperRef.current) {
innerRef.current = wrapperRef.current.querySelector('a')
// innerRef.current is now the first <a> tag inside <OutboundLink>
}
})
return <div>
<div ref={wrapperRef}>
<OutboundLink {...props}>
{children}
</OutboundLink>
</div>
</div>
Codepen example (view the console)
But really, this is a bit of a hack. You should use a component that handles this better. Look into how hard it would be to write this component yourself from scratch. It may be trivial, and worth it.

Categories