Clone and rename a component - javascript

I have a component that is used often and for many porpuses - <Box />.
I'd like to rename it to declare it's functional meaning in the code, something along these lines:
import Box from '../reusable-components/Box'
const Wrapper = Box
const Card = Box
const TopSection = Box
// Or like this?
// const [Wrapper, Card, TopSection] = cloneComponent(Box)
function MyComponent() {
return (
<Wrapper>
<TopSection style={{display: 'flex'}}>
<Card>Item A</Card>
<Card>Item B</Card>
<Card>Item C</Card>
</TopSection>
</Wrapper>
)
}

If its a react component, there is no point "cloning" it. Since When you use it in , it will create a new instance. In fact I am not clear why you need to rename it at all, but here is how you can do it
import Wrapper from '../reusable-components/Box'
import Card from '../reusable-components/Box'
import TopSection from '../reusable-components/Box'
//Or
const [Wrapper, Card, TopSection] = [Box,Box,Box]
function MyComponent() {
return (
<Wrapper>
<TopSection style={{display: 'flex'}}>
<Card>Item A</Card>
<Card>Item B</Card>
<Card>Item C</Card>
</TopSection>
</Wrapper>
)
}

Related

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

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 !

Implementing a function to swap array elements in React without mutating the original array

I am coding a react app in which a user can click a button to swap an item in an array with the item to its left. I wrote a function to implement this without mutating the original items array that is rendering on the page, but this function is not doing anything to my code, nor is it returning any errors.
Here is my app component, which defines the function swapLeft then passes that function down to the Item component as props:
import React, { useState } from "react";
import Form from "./components/Form";
import Item from "./components/Item";
import { nanoid } from "nanoid";
import './App.css';
function App(props) {
const [items, setItems] = useState(props.items);
function deleteItem(id) {
const remainingItems = items.filter(item => id !== item.id);
setItems(remainingItems);
}
function swapLeft(index) {
const index2 = index - 1;
const newItems = items.slice();
newItems[index] = items[index2];
newItems[index2] = items[index];
return newItems;
}
const itemList = items
.map((item, index) => (
<Item
id={item.id}
index={index}
name={item.name}
key={item.id}
deleteItem={deleteItem}
swapLeft={swapLeft}
/>
));
function addItem(name) {
const newItem = { id: "item-" + nanoid(), name: name };
setItems([...items, newItem]);
}
return (
<div className="form">
<Form addItem={addItem} />
<ul className="names">
{itemList}
</ul>
</div>
);
}
export default App;
And the Item component:
import React from "react";
import { Button, Card, CardContent, CardHeader } from 'semantic-ui-react'
export default function Item(props) {
return (
<Card>
<CardContent>
<CardHeader> {props.name}</CardHeader>
<Button onClick={() => props.deleteItem(props.id)}>
Delete <span className="visually-hidden"> {props.name}</span>
</Button>
</CardContent>
<CardContent style={{ display: 'flex' }}>
<i className="arrow left icon" onClick={() => props.swapLeft(props.index)} style={{ color: 'blue'}}></i>
<i className="arrow right icon" style={{ color: 'blue'}}></i>
</CardContent>
</Card>
);
}
Is there a better way for me to write this function and implement this? I suppose I could do something with the React setState hook, but this seemed like an easier solution. I am new to React so any insight would be helpful
The way React knows if the state has changed is whether the state is refers to an entirely different address in memory. In case of arrays, if you want React to rerender the page because the array in the state changed, you need to provide it an entirely new array. Modifying the existing array will not trigger the render process.
Basically, what you need to do is changed the last line of swapLeft function to
setItems(newItems)
If you want the changes to take effect immediately (which is what I guess you want to do here)
You can also use the return value from the function and change the state in another component, FYI.
EDIT:
I looked at this again, and your implementation of swap is also wrong, but even if you corrected it you still wouldn't see a change, unless you did what I mentioned above
The full correct function would be
function swapLeft(index) {
const index2 = index - 1;
const newItems = items.slice();
const temp = items[index];
newItems[index] = items[index2];
newItems[index2] = temp;
setItems(newItems);
}
Just to maybe clarify the previous one. If you don't call setState, your component doesn't rerender. This means that no matter what you do with those arrays, it won't be visible on the screen.

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>
);

emotion style inner component

I have a base Component without theme and I wish to theme it and its children, is it possible to do with Emotion?
let's I have:
BaseComponent.jsx
const Wrapper = styled('div')``;
const Inner1 = styled('div')``;
const Inner2 = styled('div')``;
export MyBaseComponent = () => (
<Wrapper>
<Inner1 />
<Inner2 />
</Wrapper>
);
ThemedMyComponent.jsx
export ThemeMyComponent = () => {
???
return (
<MyBaseComponent ??? ??? ??? />
)
}
all my theme stuff are in the second component and I would like to complex logic and decorate the BaseComponent only inside ThemedMyComponent.
initial approach would be:
const Wrapper = styled('div')``;
const Inner1 = styled('div')``;
const Inner2 = styled('div')``;
export MyBaseComponent = (props) => (
<Wrapper className={props.wrapperClassName}>
<Inner1 className={props.inner1ClassName}/>
<Inner2 className={props.inner2ClassName}/>
</Wrapper>
);
and pass 3 className to it, which means I have to use withTheme and do a lot of cx, and not mentioning, you can't not longer do this with emotion 10. is there a more functional way to do this?
Doing this, the whole thing I would need is just css from the ClassNames render callback, which make styled unused...

How to write a wrapper around a material UI component using React JS?

I am using Material UI next library and currently I am using List component. Since the library is in beta, lot of its parameter names get changed. To solve this I am planning to write a wrapper around the required components so that things wont break. My list component :
<List dense>
<List className={classes.myListStyles}>
<ListItem disableGutters/>
</List>
</List>
How should I write the wrapper for the List(say myListWrapper) and ListItem so that the wrapper component can handle props and pass them to the actual MUI list component inside?
I had worked on MUI wrappers, writing my own library for a project. The implementation we are focusing, is to pass the props to inner/actual-MUI component from the our wrapper component. with manipulation. In case of wrapping props for abstraction.
Following is my approach to the solution:
import { List as MaterialList } from 'material-ui/List';
import { React } from 'react';
import { ListItem as MaterialListI } from 'material-ui/ListItem';
class List extends MaterialList {
constructor(props){
const propsToPass = {
prop1 : change(props.prop1),
...props
}
super(propsToPass);
}
};
class ListItem extends MaterialListItem {
const propsToPass = {
prop1 : change(props.prop1),
prop2 : change(props.prop2),
...props
}
super(propsToPass);
}
};
class App extends React.Component {
render () {
return (
<List prop='value' >
<ListItem prop1={somevalue1} prop2={somevalue2} />
<ListItem prop1={somevalue1} prop2={somevalue2} />
<ListItem prop1={somevalue1} prop2={somevalue2} />
</List>
)
}
};
Above code will allow following things to do with your component:
You can use the props with exact names, as used in Material UI.
You can manipulate/change/transform/reshape you props passed from outside.
If props to you wrapper components are passed with exactly same names as MUI is using, they will directly be sent to the inner component. (... operator.)
You can use Component with exact same name as material is using to avoid confusion.
Code is written according to advance JSX and JavaScript ES6 standards.
You have a space to manipulate your props to pass into the MUI Components.
You can also implement type checking using proptypes.
You can ask for any confusion/query.
You can write it like this:
const MyList = props => (
<List
{/*mention props values here*/}
propA={props.A}
propB={props.B}
>
{props.children}
</List>
)
const MyListItem = props => (
<ListItem
{/*mention props values here*/}
propA={props.A}
propB={props.B}
>
{props.children}
</ListItem>
)
Now you need to use MyList and MyListItem, decide the prop names for these component (as per your convenient), and inside these component map those values to actual Material-UI component properties.
Note:
If you are using the same prop names (same name as material-ui component expect) for your component then you can write like this also:
const MyList = ({children, ...rest}) => <div {...rest}>{children}</div>
const MyListItem = ({children, ...rest}) => <p {...rest}>{children}</p>
Check this example:
const A = props => <div>{props.children}</div>
const B = props => <p>{props.children}</p>
ReactDOM.render(
<A>
<A>
<B>Hello</B>
</A>
</A>,
document.getElementById('app')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app' />

Categories

Resources