findDOMNode is deprecated in StrictMode. Warning - react-transition-group + react v17 + Javascript (Not Typescript) - javascript

I'm trying to get rid of a warning message in the project I'm working on.
index.js:1 Warning: findDOMNode is deprecated in StrictMode. findDOMNode was passed an instance of Transition which is inside StrictMode. Instead, add a ref directly to the element you want to reference. Learn more about using refs safely here: https://reactjs.org/link/strict-mode-find-node
at div
at Transition (http://localhost:3000/static/js/vendors~main.chunk.js:47483:30)
at CSSTransition (http://localhost:3000/static/js/vendors~main.chunk.js:46600:35)
at div
at TransitionGroup (http://localhost:3000/static/js/vendors~main.chunk.js:48052:30)
at Contacts (http://localhost:3000/static/js/main.chunk.js:1623:96)
at div
at div
at Home (http://localhost:3000/static/js/main.chunk.js:2549:88)
at AuthCheck (http://localhost:3000/static/js/main.chunk.js:2705:5)
at Routes (http://localhost:3000/static/js/vendors~main.chunk.js:45749:5)
at div
at Router (http://localhost:3000/static/js/vendors~main.chunk.js:45682:15)
at BrowserRouter (http://localhost:3000/static/js/vendors~main.chunk.js:45198:5)
at ContactState (http://localhost:3000/static/js/main.chunk.js:3743:85)
at AuthState (http://localhost:3000/static/js/main.chunk.js:3243:85)
at AlertState (http://localhost:3000/static/js/main.chunk.js:2844:85)
at App
The problematic code:
import React, { Fragment, useEffect } from 'react';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import { useContactContext } from '../../context/contact/contactContext';
import { useAuthtContext } from '../../context/auth/authContext';
import ContactItem from './ContactItem';
import Spinner from '../layout/Spinner';
const Contacts = () => {
const { contacts, filtered, getContacts, loading } = useContactContext();
const { isAuthenticated } = useAuthtContext();
useEffect(() => {
if (isAuthenticated) {
getContacts();
}
// eslint-disable-next-line
}, [isAuthenticated]);
if (!loading && contacts !== null && contacts.length === 0) {
return <h4>Please add a contact</h4>;
}
return (
<Fragment>
{contacts === null || loading ? (
<Spinner />
) : (
<TransitionGroup>
{(filtered || contacts).map((contact) => (
<CSSTransition timeout={1000} classNames="item" key={contact._id}>
<ContactItem contact={contact} />
</CSSTransition>
))}
</TransitionGroup>
)}
</Fragment>
);
};
export default Contacts;
I've spent a few hours looking for answers, but I feel like I'm running around in an endless loop.
To get rid of the warning, I need to use useRef hooks on each CSSTransition element, to connect it with (it's children?).
I can't use useRef() inside the render function of a component, so I defined a new component to display each TransitionItem:
...
const TransitionItem = ({ contact, ...props }) => {
const ref = useRef(null); // Had to use this ref to go around a warning
return (
<CSSTransition nodeRef={ref} timeout={1000} classNames="item" {...props}>
<div ref={ref}>
<ContactItem contact={contact} />
</div>
</CSSTransition>
);
};
return (
<Fragment>
{contacts === null || loading ? (
<Spinner />
) : (
<TransitionGroup>
{(filtered || contacts).map((contact) => (
<TransitionItem key={contact._id} contact={contact} />
))}
</TransitionGroup>
)}
</Fragment>
);
...
Now every time I try to click on a button, to remove an item from the list, I see a "flashing" effect, you can check out in this Sandbox: (Click on the red buttons to remove an item)
https://codesandbox.io/s/kind-feather-2psuz
The "flashing" problem only starts when I move the CSSTransition component into the new TransitionItem component, but I can't use useRef hooks on each item if I don't move it there.
Help pls! :)
PS:
Removing <React.StrictMode> from the index.js is not a solution to the root problem.

I have the same warning in my project and i can fix it with this solution, thank pixel-fixer !
Issue #668 on repo react-transition-group
From 4.4.0 release notes:
react-transition-group internally uses findDOMNode, which is
deprecated and produces warnings in Strict Mode, so now you can
optionally pass nodeRef to Transition and CSSTransition, it's a ref
object that should point to the transitioning child:
You can fix this like this
import React from "react"
import { CSSTransition } from "react-transition-group"
const MyComponent = () => {
const nodeRef = React.useRef(null)
return (
<CSSTransition nodeRef={nodeRef} in timeout={200} classNames="fade">
<div ref={nodeRef}>Fade</div>
</CSSTransition>
)
}
I hope it works for you, have a nice day !

Related

history.push() does not work on the same page using react

I am building a listing page where products are displayed and i have a search window in my header (on every page).
The search window works fine. I enter a searchword, it forwards to the listing page and it gives me the results. This works on every site, except when i am already on the listing page. If i enter a searchword while i am on the listing page, it changes the url, but nothing else.
Code Search: The Searchinput triggers the change and is a component inside Search and looks as follows:
import React, { useState, useRef } from 'react';
import { LISTING_POSTS_PAGE } from 'settings/constant';
import { HOME_PAGE } from 'settings/constant';
const SearchInput = ({history}) => {
const [searchword, setSearchword] = useState('');
const submitHandler = (e) => {
e.preventDefault();
history.search= '?'+searchword;
history.push({
pathname: LISTING_POSTS_PAGE,
})
}
return (
<form className="search" onSubmit={submitHandler}>
<div className = "row">
<input
type = "text"
searchword = "q"
id = "q"
placeholder = "What do you want to buy?"
onChange = {(e) => setSearchword(e.target.value)}>
</input>
</div>
</form>
);
};
const Search= (props) => {
console.log(props)
const { updatevalue } = props;
return <SearchInput getinputvalue={updatevalue} history={props.history} />;
};
export default Search;
The listing page looks like this and takes the history object to make an api request to my db before rendering.
import React, { useState, Fragment } from 'react';
import Sticky from 'react-stickynode';
import Toolbar from 'components/UI/Toolbar/Toolbar';
import { Checkbox } from 'antd';
import CategotySearch from 'components/Search/CategorySearch/CategotySearch';
import { PostPlaceholder } from 'components/UI/ContentLoader/ContentLoader';
import SectionGrid from 'components/SectionGrid/SectionGrid';
import ListingMap from './ListingMap';
import FilterDrawer from 'components/Search/MobileSearchView';
import useWindowSize from 'library/hooks/useWindowSize';
import useDataApi from 'library/hooks/useDataApi';
import { SINGLE_POST_PAGE } from 'settings/constant';
import ListingWrapper, { PostsWrapper, ShowMapCheckbox } from './Listing.style';
export default function Listing({ location, history }) {
let url = 'http://127.0.0.1:5000/api/products'
if (history.search) {
url = url + history.search;
}
console.log(url)
const { width } = useWindowSize();
const [showMap, setShowMap] = useState(false);
const { data, loading, loadMoreData, total, limit } = useDataApi(url);
let columnWidth = [1 / 1, 1 / 2, 1 / 3, 1 / 4, 1 / 5];
if (showMap) {
columnWidth = [1 / 1, 1 / 2, 1 / 2, 1 / 2, 1 / 3];
}
const handleMapToggle = () => {
setShowMap((showMap) => !showMap);
};
return (
<ListingWrapper>
<Sticky top={82} innerZ={999} activeClass="isHeaderSticky">
<Toolbar
left={
width > 991 ? (
<CategotySearch history={history} location={location} />
) : (
<FilterDrawer history={history} location={location} />
)
}
right={
<ShowMapCheckbox>
<Checkbox defaultChecked={false} onChange={handleMapToggle}>
Show map
</Checkbox>
</ShowMapCheckbox>
}
/>
</Sticky>
<Fragment>
<PostsWrapper className={width > 767 && showMap ? 'col-12' : 'col-24'}>
<SectionGrid
link={SINGLE_POST_PAGE}
columnWidth={columnWidth}
data={data}
totalItem={total.length}
loading={loading}
limit={limit}
handleLoadMore={loadMoreData}
placeholder={<PostPlaceholder />}
/>
</PostsWrapper>
{showMap && <ListingMap />}
</Fragment>
</ListingWrapper>
);
}
I tried to pass down the history object so i do not use different history objects (like useHistory from "react-router-dom") but it didnt changed anything on that behaviour.
I Do assume this is because i try to do history.push(LISTING_PAGE) while i am already on this page. But as far i read, this should be irrelevant. What do you think?
EDIT:
My index.js lloks as follows:
const App = () => (
<ThemeProvider theme={theme}>
<>
<GlobalStyles />
<BrowserRouter>
<AuthProvider>
<Routes />
</AuthProvider>
</BrowserRouter>
</>
</ThemeProvider>
);
React re-renders the page when a key of the component is changed. So you can do this in your router. This will make sure the key is change every time a param updates, thus result in re-render of the component.
<Route
exact
path="/your-page/:param"
render={(props) => (
<YourComponent
key={props.match.params.prodId}
/>
)}
/>
You need to add a hidden Link element in your SearchInput component. also need to create a reference and pass it to the Link element to trigger the click action on it:
import React, {useRef} from 'react';
import {Link} from 'react-router-dom';
// rest of the codes ...
// inside of SearchInput component
const linkRef = useRef(null);
return (
<form className="search" onSubmit={submitHandler}>
<div className = "row">
<Link to={LISTING_POSTS_PAGE} className={{display: "none"}} ref={linkRef} />
// rest of the codes ...
Now, it's time to change the submitHandler method to trigger a click action on the Link element after submitting the form:
const submitHandler = (e) => {
e.preventDefault();
history.search= '?'+searchword;
linkRef.current.click() // ---> instead of using history.push()
}
Note: better solution may be available, like force page to re-render and so on but using a simple concept of Link will be helpful as I explained above.

How can I print an page with react-to-print

I need a print button on my form. I was doing an research for a library to do that, and I found this one:
https://www.npmjs.com/package/react-to-print
I was thinking about this flow: a component import and add a line in my code to call that component, and the print button works well, but as I understand it this component needs me to pass the component to be printed in full.
I tried to do this, but I got this error:
Attempted import error: './index' does not contain a standard export (imported as 'FormContent').
my index code:
const App = () => {
let numb_days = 22
return (
<div className="m-4">
<FormField label="Salário Base" show_small_text="false" numb_days={ numb_days }/>
<hr />
<h6 className="mb-4"> Qtd. dias úteis: { numb_days } </h6>
<FormField label="Auxilio Refeição" show_small_text="true" numb_days={ numb_days }/>
<FormField label="Auxilio Alimentação" show_small_text="true" numb_days={ numb_days }/>
<FormField label="Plano de Saúde" show_small_text="false" numb_days={ numb_days }/>
<FormField label="Outros Benefìcios (VT)" show_small_text="true" numb_days={ numb_days }/>
<ComponentToPrint ref={(el) => (this.componentRef = el)} />
</div>
);
};
my componente code:
import React, { Component } from "react";
import FormContent from "./index";
class ComponentToPrint extends Component {
render() {
return <FormContent ref={(el) => (this.componentRef = el)} />;
}
}
export default ComponentToPrint;
I think I must be making a big mistake, but I don't understand how I'm going to pass my index on to this component and call my index at the same time.
I found this example: https://codesandbox.io/s/interesting-cookies-k1bg9?file=/src/deliverySheet/ComponentToPrint.js
it looks like I need to follow the flow:
index -> print (my content).
but why couldn’t I do that? ->
index -> my content (print)
or
index -> my content AND print
I'm not really sure I understand your question but I'm going to try to answer it, the examples you gave is really hard to read because of all of the subfolders. Basicly what you need to do to print whit react to print is make a normal component and reference it on the same level.
I see you use class components, so im just going to copy and paste from the react-to-print docs.
import React from 'react';
import ReactToPrint from 'react-to-print';
import { ComponentToPrint } from './ComponentToPrint';
class Example extends React.PureComponent {
render() {
return (
<div>
<ReactToPrint
trigger={() => {
// NOTE: could just as easily return <SomeComponent />. Do NOT pass an `onClick` prop
// to the root node of the returned component as it will be overwritten.
return Print this out!;
}}
content={() => this.componentRef}
/>
<ComponentToPrint ref={el => (this.componentRef = el)} />
</div>
);
}
}
Where it says "trigger" is where you render your button.
I hope that helped.

HOC for JSX element - rendering jsx with wrapped element

I want to call a ReactJS HOC to wrap a tooltip around JSX.
The call should be able like this:
withTooltip(JSX, "very nice")
Therefor I have created this function:
import React from "react";
import MUITooltip from "#material-ui/core/Tooltip";
import useStyles from "./index.styles";
const withTooltip = (Component, text: string) => (props) => {
const classes = useStyles();
return (
<MUITooltip className={classes.root} title={text}>
<Component {...props} />
</MUITooltip>
);
};
export default withTooltip;
The call:
import withTooltip from "commons/withTooltip/withTooltip";
const dialogBtn =
isOk &&
withTooltip(
<div className={classes.buttonWithLoader}>
<OpenDialogButton
variant={BaseButtonVariant.Icon}
openDialogAttributes={areas.button.openDialogAttributes}
/>
</div>,
"Very nice",
);
return (
<Fragment>
{dialogBtn}
</Fragment>
);
It says:
Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it
How to solve it ?
Your HOC accepts a Component argument while you are passing in JSX. Try wrapping the JSX with a function or pass in a component which renders the Button.
However, in your case, you probably want to have control over the toolTip text in your component. If this is the case, I would not use a HOC for this, but rather a wrapping Component.
function WithTooltip({ classes, text, children }) {
return (
<MUITooltip className={classes.root} title={text}>
{children}
</MUITooltip>
);
}
export default WithTooltip;
const dialogBtn = isOk && (
<WithTooltip text="Very nice">
<div className={classes.buttonWithLoader}>
<OpenDialogButton
variant={BaseButtonVariant.Icon}
openDialogAttributes={areas.button.openDialogAttributes}
/>
</div>
</WithTooltip>
);

Too many re-renders with React Hooks and Redux

I have a component that displays a list of Cards. I'm trying to sort the table rows but running into some issues. When i go to the page i'm getting the following error:
Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
and it's pointing to this line
setData(_.sortBy(filteredData.reverse()));
here's my full component code. can anyone see a problem with what I'm trying to do?
import React, { useState } from "react";
import Search from "./Search";
import TimeAgo from "react-timeago";
import { useSelector, useDispatch, connect } from "react-redux";
import { Table } from "semantic-ui-react";
import { searchChange } from "../reducers/searchReducer";
import _ from "lodash";
// import { useField } from "../hooks";
const searchCards = ({ baseball, search }) => {
return search
? baseball.filter(a =>
a.title[0].toLowerCase().includes(search.toLowerCase())
)
: baseball;
};
const Cards = props => {
const [column, setColumn] = useState(null);
const [direction, setDirection] = useState(null);
const [filteredData, setData] = useState(props.cardsToShow);
const handleSort = clickedColumn => {
if (column !== clickedColumn) {
setColumn(clickedColumn);
setData(_.sortBy(filteredData, [clickedColumn]));
setDirection("ascending");
return;
}
setData(_.sortBy(filteredData.reverse()));
direction === "ascending"
? setDirection("descending")
: setDirection("ascending");
};
return (
<>
<div>
<Search />
<h3>Vintage Card Search</h3>
<Table sortable celled fixed striped>
<Table.Header>
<Table.Row>
<Table.HeaderCell
sorted={column === "title" ? direction : null}
onClick={handleSort("title")}
>
Card Title
</Table.HeaderCell>
<Table.HeaderCell># Bids</Table.HeaderCell>
<Table.HeaderCell>Watchers</Table.HeaderCell>
<Table.HeaderCell>Price</Table.HeaderCell>
<Table.HeaderCell>Time Left</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{props.cardsToShow.map(card => (
<>
<Table.Row key={card.id}>
<Table.Cell>{card.title}</Table.Cell>
<Table.Cell>
{card.sellingStatus[0].bidCount
? card.sellingStatus[0].bidCount
: 0}
</Table.Cell>
<Table.Cell>
{card.listingInfo[0].watchCount
? card.listingInfo[0].watchCount
: 0}
</Table.Cell>
<Table.Cell>
$
{card.sellingStatus &&
card.sellingStatus[0].currentPrice[0]["__value__"]}
</Table.Cell>
<Table.Cell>
<TimeAgo
date={new Date(
card.listingInfo && card.listingInfo[0].endTime
).toLocaleDateString()}
/>
</Table.Cell>
</Table.Row>
</>
))}
</Table.Body>
</Table>
</div>
</>
);
};
const mapStateToProps = state => {
return {
baseball: state.baseball,
search: state.search,
cardsToShow: searchCards(state)
};
};
const mapDispatchToProps = {
searchChange
};
export default connect(mapStateToProps, mapDispatchToProps)(Cards);
// export default Cards;
Yachaka has already pointed out the incorrect line, but their answer doesn't explain what the issue is.
When you pass props in React with prop={expression}, the expression in the brackets gets evaluated, much like function arguments are evaluated when they are passed. Hence, whenever the component is rendered, handleSort("title") is called. This function then causes the props to be updated, and the component is re-rendered, causing the cycle to repeat indefinitely.
So the problem is that, instead of passing a function that should be called when the button is clicked, you call that function (with handleSort("title")), which results in undefined, and causes a feedback loop.
Instead you should use an expression that returns a function. The most concise way of doing that in JavaScript is an arrow function, as Yachaka mentioned () => handleSort("title"). This evaluates to a function that calls handleSort.
Change this line:
onClick={handleSort("title")}
by
onClick={() => handleSort("title")}
EDIT: Reinis has written a nice explanation below!

How to use shouldComponentUpdate with React Hooks?

I've been reading these links:
https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-shouldcomponentupdate
https://reactjs.org/blog/2018/10/23/react-v-16-6.html
In the first link it says (https://reactjs.org/docs/hooks-faq.html#from-classes-to-hooks):
shouldComponentUpdate: See React.memo
The second link also states that:
Class components can bail out from rendering when their input props are the same using PureComponent or shouldComponentUpdate. Now you can do the same with function components by wrapping them in React.memo.
What is desired:
I want Modal to render only when the Modal is visible (managed by this.props.show)
For class component:
shouldComponentUpdate(nextProps, nextState) {
return nextProps.show !== this.props.show;
}
How can I use memo instead in a functional component - here, in Modal.jsx?
The related code:
Functional component Modal.jsx (I don't know how to check for props.show)
import React, { useEffect } from 'react';
import styles from './Modal.module.css';
import BackDrop from '../BackDrop/BackDrop';
const Modal = React.memo(props => {
useEffect(() => console.log('it did update'));
return (
<React.Fragment>
<BackDrop show={props.show} clicked={props.modalClosed} />
<div
className={styles.Modal}
style={{
transform: props.show ? 'translateY(0)' : 'translateY(-100vh)',
opacity: props.show ? '1' : '0'
}}>
{props.children}
</div>
</React.Fragment>
);
});
export default Modal;
The part of class component PizzaMaker jsx that renders Modal:
return (
<React.Fragment>
<Modal show={this.state.purchasing} modalClosed={this.purchaseCancel}>
<OrderSummary
ingredients={this.state.ingredients}
purchaseCancelled={this.purchaseCancel}
purchaseContinued={this.purchaseContinue}
price={this.state.totalPrice}
/>
</Modal>
...
</React.Fragment>
);
Here is the documentation for React.memo
You can pass a function to control the comparison :
const Modal = React.memo(
props => {...},
(prevProps, nextProps) => prevProps.show === nextProps.show
);
when the function returns true, the component will not be re-rendered
Also you can use in export statement like:
export default memo(Modal, (prevProps, nextProps) => prevProps.show === nextProps.show) ;

Categories

Resources