ReactJS - PropTypes not validating - reactjs

PropTypes is not validationg (ignore!!!) this simple component, I tried everything but it doesn't work.
import React, { useEffect, useState } from "react";
import PropTypes from "prop-types";
import Item from "./Item";
import chunkArray from "../../utils/chunkArray";
const AllItems = (props) => {
const { items, title } = props;
const [arrayToMap, setArrayToMap] = useState([]);
// set size array based on columns
const arrSize = 12;
// chunk array
useEffect(() => {
setArrayToMap(chunkArray(items, arrSize));
}, [items]);
return (
<>
{arrayToMap.length === 0 ? (
<div>Wait</div>
) : (
<>
{title}
{arrayToMap.map((x) => {
return (
<div className="row">
{x.map((y, i) => (
<div className="col-1" key={i}>
<Item item={y} size={"small"} />
</div>
))}
</div>
);
})}
</>
)}
</>
);
};
AllItems.propTypes = {
items: PropTypes.array.isRequired,
title: PropTypes.string.isRequired,
};
export default AllItems;
I can pass whatever to ìtems or title but doesn't stop the render.
Any idea?
Thanks!

rerendering your component is not about proptypes, it's because you set state in your useEffect function without any condition. so the component will work in an infinite loop of rendering. you can read about it here
you need to add some condition to set your state in once like this :
useEffect(() => {
if(arrayToMap.length === 0 && items.length > 0){
setArrayToMap(chunkArray(items, arrSize));
}
}, [items]);
so if your state is clear and your items array is not empty so the condition will work.

The reason you are not seeing the warnings, Is because you're using props as the param in your component. Change your component to the following
const AllItems = ({ items, title }) => {}
Proptypes doesn't stop component from rendering but you can use eslint to validate the params and stop the app from starting. Add eslintrc.json file to your project and add the following rule.
"react/prop-types": 2,

Related

Cannot read values from array object from the map function in React

I am trying to pass value from an array items object to another component using map(). All values is coming from api call and is showed in console.But when passing the value from here to Titlecard, I am getting error cannot read properties of undefined map()?I have given the code.Also I have shared Titlecard. Always passing only one record into the array Can anyone guide me here? Thanks
import axios from "axios";
import React, { useEffect, useState } from "react";
import { Container } from "react-bootstrap";
import Titlecard from "./Titlecard";
import { HeroesUrl } from "../apiurl/HeroesApi";
const TitleHero = () => {
const [items, setItems] = useState([]);
useEffect(() => {
axios.get(HeroesUrl).then((response) => {
setItems(response.data);
console.log(response.data);
});
}, []);
return (
<>
<Container>
<div>
{items.map((item) => {
return (
<>
<Titlecard key={item.id} item={item} />
</>
);
})}
</div>
</Container>
</>
);
};
export default TitleHero;
import React, { useEffect, useState } from "react";
const Titlecard = (item) => {
return (
<>
<div> item.firstName </div>
</>
);
};
export default Titlecard;
I edit my answer after I saw you shared Titlecard component.
There are 2 problems.
The first is in your return, it should be:
<div>{item.firstName}</div>
Because what you return before is just a string like "item.firstName".
Second, you have to make a destructuring to the props in Titlecard like:
const Titlecard = ({item}) => {...}
or return:
<div>{item.item.firstName}</div>
The first one is your props name, the second is the prop you pass.
So, with using destructuring Titlecard should be like this:
import React from "react";
const Titlecard = ({item}) => {
return (
<>
<div>{item.firstName}</div>
</>
);
};
export default Titlecard;
Please share Titlecard component code.
It's look like that there is a part in the Titlecard component that use the item from the map. In the first time before the axios call finished, the prop item is still empty array, so if you use in the Titlecard component item.something you will get an undefined error.
One solution is to add a loader that initial to true, and after the axios call finished set it to false, so if the loader is true, render a loader, else render your map code.
Another solution is adding ? when you use item in Titlecard component, like: item?.something, what means only if item is not null or undefined.

Keep track of another components state

I have a bit of a basic React question that I am having trouble googling.
I have this component which is managing the state of maximize:
import React from 'react'
import { useState } from 'react';
import './Panel.scss'
import { AiFillExperiment, AiOutlineExpandAlt } from "react-icons/ai";
const Panel = ({title}) => {
const [maximize, setMaximize] = useState(false);
return (
<div className='panel'>
<AiFillExperiment />
<p>{title}</p>
<AiOutlineExpandAlt onClick={() => setMaximize(!maximize)} />
</div>
)
}
export default Panel
and this component that needs to be able to see the value of that state:
import './App.scss';
import { useEffect, useState } from 'react';
import ReactMarkdown from 'https://esm.sh/react-markdown#7'
import remarkBreaks from 'https://esm.sh/remark-breaks#3'
import Panel from './components/Panel'
function App() {
const [markdown, setMarkdown] = useState(``)
const placeholder =
`# Welcome to my React Markdown Previewer!
## This is a sub-heading...
### And here's some other cool stuff:
Here's some code, \`<div></div>\`, between 2 backticks.
\`\`\`
// this is multi-line code:
function anotherExample(firstLine, lastLine) {
if (firstLine == '\`\`\`' && lastLine == '\`\`\`') {
return multiLineCode;
}
}
\`\`\`
You can also make text **bold**... whoa!
Or _italic_.
Or... wait for it... **_both!_**
And feel free to go crazy ~~crossing stuff out~~.
There's also [links](https://www.freecodecamp.org), and
> Block Quotes!
And if you want to get really crazy, even tables:
Wild Header | Crazy Header | Another Header?
------------ | ------------- | -------------
Your content can | be here, and it | can be here....
And here. | Okay. | I think we get it.
- And of course there are lists.
- Some are bulleted.
- With different indentation levels.
- That look like this.
1. And there are numbered lists too.
1. Use just 1s if you want!
1. And last but not least, let's not forget embedded images:
![freeCodeCamp Logo](https://cdn.freecodecamp.org/testable-projects-fcc/images/fcc_secondary.svg)
`;
useEffect(() => {
setMarkdown(placeholder)
}, [placeholder])
return (
<div className="App">
{/* Editor Container */}
<div
className={'editor-container'}
>
<Panel title='Editor' />
<textarea id='editor' onChange={(e) => setMarkdown(e.target.value)} rows="" cols="">{placeholder}</textarea>
</div>
{/* Preview Container */}
<div className='preview-container'>
<Panel title='Preview' />
<div id='preview'>
<ReactMarkdown children={markdown} remarkPlugins={[remarkBreaks]} />
</div>
</div>
</div>
);
}
export default App;
How do I go about doing this? I realize I could have it all in one component, but I would like to know how to do it with two separate components.
Thanks in advance!
Through useState + props (less recommended)
You can do that by having that state in your App component and passing the setState as a property
const App = () => {
const [maximize, setMaximize] = useState(false);
const handleToggle = (newState) => {
setState(newState)
}
return (
<div>
<Panel toggleState={toggleState} maximize={maximize} />
</div>
)
}
And in your Panel component:
const Panel = ({toggleState, maximize}) => {
const handleToggle = () => {
toggleState(!maximize)
}
return (
<AiOutlineExpandAlt onClick={handleToggle} />
)
}
Through useContext hook
useContext allows you to store variables and access them on all child components within that context provider.
MaximizeProvider.js
import React, {useState, useContext} from "react";
//creating your contexts
const MaximizeContext = React.createContext();
const MaximizeUpdateContext = React.createContext();
// create a custom hook
export const useUpdate = () => {
return useContext(MaximizeUpdateContext)
}
export const useMaximize = () => {
return usecContext(MaximizeContext)
}
//creating your component that will wrap the child components
const MaximizeProvider = ({children}) => {
const [maximize, setMaximize] = useState(false)
// Your toggle to switch the state
const toggle = () => {
setMaximize(prevState => !prevState)
}
return (
<MaximizeContext.Provider value={maximize}>
<MaximizeUpdateContext.Provider value={toggle}>
{children}
</MaximizeUpdateContext.Provider>
</MaximizeContext.Provider>
)
}
export {MAximizeProvider}
Both providers allow you to access both the state and the setState
App.js
import React, {useState} from "react";
// your context component
import {MaximizeProvider} from "./MaximizeProvider";
// a button component
import {ButtonComponent} from "./ButtonComponent";
const App = () => {
return (
<>
<MaximizeProvider>
<ButtonComponent/>
</MaximizeProvider>
< />
);
}
export {App};
in the App, you are wrapping the elements that need your context.
as long as the elements and even children of children are in the wrap, it would have access to it the same way as in the button component.
ButtonComponent.js
import {useMaximize, useUpdate} from "./MaximizeProvider";
const ButtonComponent = () => {
const toggle = useUpdate();
const state = useMaximize()
return (
<button onClick={toggle}>Click</button>
);
}
export {ButtonComponent};
I hope this helps, I am not an expert, so there might be better ways to do it, but this seems to work for me.
Use redux or react context please,
props drilling is bad practice
https://reactjs.org/docs/context.html
https://redux.js.org/

List Rendering in React JS

import React,{useState} from 'react'
import App from './App'
const AppList = ()=>{
const [arr,addElement] = useState([1])
const handleClick = ()=>{
arr.push(1)
addElement(arr)
}
return(
<>
{
arr.map((elements)=>{
return <h1>{elements}</h1>
})
}
<button onClick={handleClick}>add</button>
</>
)
}
export default AppList
I tried to add elements and render. the items are getting added in array but its not getting rendered in browser.
React is checking state updates by reference, since you provide the same array reference the component is not going to re-render.
Instead, make an immutable update
const AppList = () => {
const [arr, addElement] = useState([1])
const handleClick = () => {
addElement(prevState => [...prevState, 1])
}
return (
<>
{arr.map(element => (
<h1>{element}</h1>
))}
<button onClick={handleClick}>add</button>
</>
)
}
You also need to provide a unique key when rendering arrays, but don't use index as key since it can cause problems when adding and removing items from the array. You don't have anything unique here so you will need to think of a way of solving it. You can find more info about it here
to rerender somthing in React you need to change the state,
the best way to do it is to pass NEW array to the state, Thats why
you have the setState method.
So, you just need:
import React,{useState} from 'react'
import App from './App'
const AppList = ()=>{
const [arr,setArray] = useState([1])
const handleClick = ()=>{
let newArr = [...arr,1]
setArray(newArr)//setArray will be a better name.
}
return(
<>
{
arr.map((elements)=>{
return <h1>{elements}</h1>
})
}
<button onClick={handleClick}>add</button>
</>
)
}
export default AppList

unable mapping through import react components using hooks

Im new to react so this may be a noob question.
Im trying create a side bar that only displays links depending on if a variable is set.
but first im just trying to create a useState hook that is an array of import components and map through them with some dummy data.
The problem is it keeps tell me the component is undefined when trying to map through them.
error:TypeError: Cannot read property 'SideBarLink' of undefined
What am I doing wrong?
Also if there is a better way to do what I am trying to please lmk.
SideBar.jsx------------------------------------------------------
]
import React, { useState } from 'react';
import SideBarLink from 'react';
const SideBar = () => {
const [sidelinks, setSideLinks] = useState( SideBarLink[
{
id: 1,
name: 'Projects',
displayLink: true
},
{
id: 2,
name: 'Tickets',
displayLink: true
}
]);
const handleClick =() =>
{
console.log(sidelinks);
}
let slinks = (
<div>
<button onClick={handleClick}>button</button>
<div className="w3-sidebar w3-bar-block" >
{
sidelinks.SideBarLink.map((SideBarLink, index) =>
{
return <SideBarLink />
})
}
</div>
</div>
);
return slinks;
}
export default SideBar;
SideBarLink.jsx-------------------------------------
import React from 'react';
const SideBarLink = ( props ) => {
return (
{props.name}
)
};
export default SideBarLink;

ReactJS - toggling a class from one component, force the other component to rerender

I'm trying to figure out how to get a component to re-render after I have changed a state value that the class list is dependent on. I'm assuming because it is not a direct property of the component, it is not re-rendering for me.
My Details component that I want to re-render when the showDetails state variable changes:
import React, { useContext } from 'react';
import { ScheduleContext } from '../../schedule-context';
const Details = () => {
const showDetails = useContext(ScheduleContext).showDetails;
const className =
'col details-col' + (showDetails ? '' : ' d-none');
return <div className={className}>details...</div>;
};
export default Details;
That state variable is set in a different component that is essentially an aunt/uncle from the Details component:
Order Component partial code:
const toggleDetails = () => {
setShowDetails((showDetails) => !showDetails);
};
return (
<Draggable draggableId={orderID} index={props.index}>
{(provided, snapshot) => (
<MyOrder
className={'order'}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
isDragging={snapshot.isDragging}
onClick={toggleDetails}
From my reading forcing a re-render of a component is frowned upon. So I'm assuming there is a way to have this cleanly automatically re-render when I update that state variable.
From the Details parent
import React, { useContext } from 'react';
import { ScheduleContext } from '../../schedule-context';
const SmartComponent = () => {
const showDetails = useContext(ScheduleContext).showDetails;
return <Details showDetails />;
};
export default Details;
// Dumb component
const Details = ({ showDetails }) => {
const className = 'col details-col' + (showDetails ? '' : ' d-none');
return <div className={className}>details...</div>;
};
It's a good pratice to have smart components that handle dumb components

Resources