React test if component contains another component - javascript

I have loading page:
const Loading: React.FunctionComponent = () => {
return (
<div className="w-screen h-screen overflow-hidden">
<LoadingBackground />
</div>
);
};
How can I test if LoadingBackground is rendered or not?
import { render } from "#testing-library/react";
import Loading from "./Loading";
import LoadingBackground from "../../components/LoadingBackground";
test("Loading", () => {
const parent = render(<Loading />);
expect(parent).toContainElement(loadingBackground); <-- Check if LoadingBackground is rendered or not?
});

You need to add something to LoadingBackground that can be used to identify it. If you don't have any unique text or so, you can add data-testid={'loadingBackground'} as an attribute to your element and do something like this:
within(parent).getByTestId('loadingBackground')

Related

Why is my JS function only applying CSS to a single instance of my React component?

I have a component named "ProjectCard" and a component named "Work". In the component Work, I have created a function get_width() that gets the width of the screen and I am running that function in the useEffect Hook. I am also using useState hook that gets set inside the useEffect Hook and I am passing the state as a prop the ProjectCard Component that I am calling inside the Work Components , I have 6 of them. In the ProjectCard components, I have a function changeStyle(dType) that gets the prop and checks if it is 'Mobile'. If the condition becomes true, It gets the html element by ID and apply the CSS to it.
Now, the problem is that I have 6 ProjectCard Components inside my Work Component , but the CSS is being applied to only one of them. Can someone kindly explain this?
Work.js
import React , {useState , useEffect} from 'react';
import Navbar from './Navbar';
import ProjectCard from './ProjectCard';
import './Work.css';
function get_width()
{
var width = window.screen.availWidth;
console.log(width);
return width;
}
function Work()
{
const [device , setDevice] = useState('Desktop');
useEffect(()=>{
if(get_width() <= 450)
{
setDevice('Mobile');
}
} , [setDevice])
return(
<>
<Navbar/>
<div id="workMain">
<h1>Our Work</h1>
<div id="workButtons">
<button>All</button>
<button>Website</button>
<button>Mobile App</button>
</div>
<div id="cards">
<ProjectCard Device = {device}/>
<ProjectCard Device = {device}/>
<ProjectCard Device = {device}/>
<ProjectCard Device = {device}/>
<ProjectCard Device = {device}/>
<ProjectCard Device = {device}/>
</div>
</div>
</>
);
}
export default Work;
ProjectCard.js
import React , {useEffect} from 'react';
import './ProjectCard.css';
function changeStyle(dType)
{
if(dType === 'Mobile')
{
var card = document.getElementById("card");
card.style.marginLeft = 0;
}
console.log(dType);
}
function ProjectCard(props)
{
useEffect(()=>{
changeStyle(props.Device);
})
return(
<>
<div id="card">
<p>Website</p>
<p>RA Traders</p>
</div>
</>
);
}
export default ProjectCard;
This statement is causing the undesired behaviour:
var card = document.getElementById("card");
With getElementById Javascript looks for an element with id "card", inside your document. This is not confined to the react component, mind you. The whole DOM is searched and the first matching instance is returned. The first matching element will be the same no matter which component the function is called from). Note, ideally id should be unique in the document.
You can use refs here (React's recommended way for DOM manipulation). With hooks you have to use useRef
function changeStyle(dType, divElement)
{
if(dType === 'Mobile')
{
divElement.current.style.marginLeft = 0;
}
console.log(dType);
}
function ProjectCard(props)
{
const divRef = useRef();
useEffect(()=>{
changeStyle(props.Device,divRef);
})
return(
<>
<div ref={divRef}>
<p>Website</p>
<p>RA Traders</p>
</div>
</>
);
}
export default ProjectCard;
divRef.current will hold the value of the DOM node and you can make changes accordingly

Next.js renders element twice

On the first image you can see next.js rendered this element twice
I used tables and thought that it is because of them but then I tried to remove tables and put jut and it still renders twice so I don't know what it can be.
Next.js does not renders only that element but the first from this object
const Sections = {
1: Locations,
0: Departments, // here it will render this one twice
2: Managers,
3: JobTitles,
};
Maybe it has something to do with useState and my statemanagment in this code below
Component that renders twice.
const Locations = () => {
return <div>hdjsad</div>;
};
// Tab Sections
import Locations from ''
import Departments from ''
import Managers from ''
import JobTitles from ''
import Icons from "../../Icons";
import TabItem from "./TabItem";
const tabs_text = ["Locations", "Departments", "Managers", "Job Titles"];
const Sections = {
0: Locations, // THIS IS THE COMPONENT WHICH RENDERS TWICE
1: Departments,
2: Managers,
3: JobTitles,
};
const SettingsTab = () => {
const [active, setActive] = useState<number>(0);
const select = useCallback((id: number) => {
return () => setActive(id);
}, []);
const ActiveSection = useMemo(() => Sections[active], [active]);
return (
<section className={"mb-[24px]"}>
<header
className={"w-full flex items-center mb-[34px] pl-[24px] pr-[12px]"}
>
<div className={"flex space-x-[8px] !mb-0 overflow-x-scroll"}>
{tabs_text.map((tab_text, i) => {
return (
<div onClick={select(i)} key={i}>
<TabItem active={+active === i}>{tab_text}</TabItem>
</div>
);
})}
</div>
<ImportLocationsAndFilter />
</header>
<ActiveSection />
</section>
);
};
APP.js
import { AppProps } from "next/app";
import "antd/dist/antd.css";
import "../styles/global.css";
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
export default MyApp;
I can't comment yet so I'll do it here. I know react says in the official docs to never rely on UseMemo or Use callback for functionality. It says you should create your application so it works without them, and then add them for performance reasons. What would happen if you took the useMemo out and put
ActiveSelection = Selections[active]
I don't think it'll fix your problem but it might give you more insight into what's causing it.
I just imported my tabs dynamically and set SSR: false.
It has to do something with next.js hydration.
https://nextjs.org/docs/advanced-features/dynamic-import
dynamic(
() => import(""),
{
ssr: false,
}
);
It's strange behaviour / bug related to next.js ssr to fix it wrap your Component in a div like this:
function MyApp({ Component, pageProps }: AppProps) {
return <div id=#root><Component {...pageProps} /></div>;
}

Learning React: how to useRef in custom component?

I'm learning React and I don't think I understand the concept of useRef properly. Basically, I want to include some tags in tagify input field when a user clicks on a chip that is rendered outside the input box.
My idea is to do something like this (App.js):
import Chip from '#material-ui/core/Chip';
import Tagify from "./Tagify"
...
class App extends React.Component {
...
const { error, isLoaded, quote, tags } = this.state; //tags comes from the server
var tagify = <Tagify tags={tags} />
const addTagOnChipClick = (tag) => {
tagify.addTag(tag)
};
const chips = tags.map(tag => (
<span key={tag.name} className="chips">
<Chip
label={tag.name}
variant="outlined"
onClick={addTagOnChipClick(tag)}
clickable
/>
</span>
))
...
}
The tagify documentation says that
To gain full access to Tagify's (instance) inner methods, A custom ref can be used: <Tags tagifyRef={tagifyRef} ... />
My attempt to gain access to these inner methods was to use useRef (Tagify.js):
import Tags from '#yaireo/tagify/dist/react.tagify'
import '#yaireo/tagify/dist/tagify.css'
export default function Tagify(tags) {
const tagifyRef = useRef()
return (
<Tags
tagifyRef={tagifyRef}
placeholder='Filter by tags...'
whitelist={tags.tags}
/>
)
}
However, tagifyRef.current is undefined. What I'm doing wrong? There's another way to access the inner methods?
Thank you very much!
When are you accessing the ref? Make sure you access the ref only after the component has mounted i.e. in a useEffect:
import Tags from '#yaireo/tagify/dist/react.tagify'
import '#yaireo/tagify/dist/tagify.css'
export default function Tagify(tags) {
const tagifyRef = useRef()
React.useEffect(() => {
console.log(tagifyRef.current)
}, [])
return (
<Tags
tagifyRef={tagifyRef}
placeholder='Filter by tags...'
whitelist={tags.tags}
/>
)
}

React Component div id will not hide what used in map loop

I have this code in an index.html file which loads with the application:
$(window).on('load', () => {
$('#one').hide();
$('#oneb').hide();
});
And this affects the component:
import React from 'react';
const Test = (props) => <div id="MyDiv">
<div>
<div id="one">
THIS IS ONE
</div>
{
props.list.map((data, i) => {
return <div id="oneb" key={i}>
THIS IS ONEB
</div>
})
}
</div>
</div>
What is happening here is that div with id="one" will hide BUT id="oneb" will still show up.
Why is this happening? How can I fix this?
You are not doing it the React way. To do it the React way, your component should hold and manipulate some kind of state. Check the example below:
import React from 'react';
class MyAwesomeComponent React.Component {
constructor(){
this.state = {
hide: false
}
}
render(){
const {hide} = this.state;
<React.Fragment>
{
hide
? null
: <div>This is my awesome div that I need to show or hide ;)</div>
}
<button>{hide ? 'Show': 'Hide'}</button>
</React.Fragment>
}
}
export default MyAwesomeComponent;
The code above will hide or show your div. Check the React documentation

Functions are not valid as a React child. This may happen if you return a Component instead of from render

I have written a Higher Order Component:
import React from 'react';
const NewHOC = (PassedComponent) => {
return class extends React.Component {
render(){
return (
<div>
<PassedComponent {...this.props}/>
</div>
)
}
}
}
export default NewHOC;
I am using the above in my App.js:
import React from 'react';
import Movie from './movie/Movie';
import MyHOC from './hoc/MyHOC';
import NewHOC from './hoc/NewHOC';
export default class App extends React.Component {
render() {
return (
<div>
Hello From React!!
<NewHOC>
<Movie name="Blade Runner"></Movie>
</NewHOC>
</div>
);
}
}
But, the warning I am getting is:
Warning: Functions are not valid as a React child. This may happen if
you return a Component instead of <Component /> from render. Or maybe
you meant to call this function rather than return it.
in NewHOC (created by App)
in div (created by App)
in App
The Movie.js file is:
import React from "react";
export default class Movie extends React.Component{
render() {
return <div>
Hello from Movie {this.props.name}
{this.props.children}</div>
}
}
What am I doing wrong?
I did encounter this error too because I didn't use the correct snytax at routing. This was in my App.js under the <Routes> section:
False:
<Route path="/movies/list" exact element={ MoviesList } />
Correct:
<Route path="/movies/list" exact element={ <MoviesList/> } />
So now the MoviesList is recognized as a component.
You are using it as a regular component, but it's actually a function that returns a component.
Try doing something like this:
const NewComponent = NewHOC(Movie)
And you will use it like this:
<NewComponent someProp="someValue" />
Here is a running example:
const NewHOC = (PassedComponent) => {
return class extends React.Component {
render() {
return (
<div>
<PassedComponent {...this.props} />
</div>
)
}
}
}
const Movie = ({name}) => <div>{name}</div>
const NewComponent = NewHOC(Movie);
function App() {
return (
<div>
<NewComponent name="Kill Bill" />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<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="root"/>
So basically NewHOC is just a function that accepts a component and returns a new component that renders the component passed in. We usually use this pattern to enhance components and share logic or data.
You can read about HOCS in the docs and I also recommend reading about the difference between react elements and components
I wrote an article about the different ways and patterns of sharing logic in react.
In my case i forgot to add the () after the function name inside the render function of a react component
public render() {
let ctrl = (
<>
<div className="aaa">
{this.renderView}
</div>
</>
);
return ctrl;
};
private renderView() : JSX.Element {
// some html
};
Changing the render method, as it states in the error message to
<div className="aaa">
{this.renderView()}
</div>
fixed the problem
I encountered this error while following the instructions here: https://reactjs.org/docs/add-react-to-a-website.html
Here is what I had:
ReactDOM.render(Header, headerContainer);
It should be:
ReactDOM.render(React.createElement(Header), headerContainer);
I had this error too. The problem was how to call the function.
Wrong Code:
const Component = () => {
const id = ({match}) => <h2>Test1: {match.params.id}</h2>
return <h1>{id}</h1>;
};
Whereas id is a function, So:
Correct code:
return <h1>{id()}</h1>;
Adding to sagiv's answer, we should create the parent component in such a way that it can consist all children components rather than returning the child components in the way you were trying to return.
Try to intentiate the parent component and pass the props inside it so that all children can use it like below
const NewComponent = NewHOC(Movie);
Here NewHOC is the parent component and all its child are going to use movie as props.
But any way, you guyd6 have solved a problem for new react developers as this might be a problem that can come too and here is where they can find the solution for that.
I was able to resolve this by using my calling my high order component before exporting the class component. My problem was specifically using react-i18next and its withTranslation method, but here was the solution:
export default withTranslation()(Header);
And then I was able to call the class Component as originally I had hoped:
<Header someProp={someValue} />
it also happens when you call a function from jsx directly rather than in an event. like
it will show the error if you write like
<h1>{this.myFunc}<h2>
it will go if you write:
<h1 onClick={this.myFunc}>Hit Me</h1>
I was getting this from webpack lazy loading like this
import Loader from 'some-loader-component';
const WishlistPageComponent = loadable(() => import(/* webpackChunkName: 'WishlistPage' */'../components/WishlistView/WishlistPage'), {
fallback: Loader, // warning
});
render() {
return <WishlistPageComponent />;
}
// changed to this then it's suddenly fine
const WishlistPageComponent = loadable(() => import(/* webpackChunkName: 'WishlistPage' */'../components/WishlistView/WishlistPage'), {
fallback: '', // all good
});
In my case, I was transport class component from parent and use it inside as a prop var, using typescript and Formik, and run well like this:
Parent 1
import Parent2 from './../components/Parent2/parent2'
import Parent3 from './../components/Parent3/parent3'
export default class Parent1 extends React.Component {
render(){
<React.Fragment>
<Parent2 componentToFormik={Parent3} />
</React.Fragment>
}
}
Parent 2
export default class Parent2 extends React.Component{
render(){
const { componentToFormik } = this.props
return(
<Formik
render={(formikProps) => {
return(
<React.fragment>
{(new componentToFormik(formikProps)).render()}
</React.fragment>
)
}}
/>
)
}
}
What would be wrong with doing;
<div className="" key={index}>
{i.title}
</div>
[/*Use IIFE */]
{(function () {
if (child.children && child.children.length !== 0) {
let menu = createMenu(child.children);
console.log("nested menu", menu);
return menu;
}
})()}
In my case I forgot to remove this part '() =>'. Stupid ctrl+c+v mistake.
const Account = () => ({ name }) => {
So it should be like this:
const Account = ({ name }) => {
In my case
<Link key={uuid()} to="#" className="tag">
{post.department_name.toString}
</Link>
changed with
<Link key={uuid()} to="#" className="tag">
{post.department_name.toString()}
</Link>
You should use
const FunctionName = function (){
return (
`<div>
hello world
<div/>
`
)
};
if you use Es6 shorthand function it will give error use regular old javascript function.

Categories

Resources