List Rendering in React JS - reactjs

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

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/

Is there a reason to not import/export state in place of prop drilling or useContext in react?

Is it possible to do something like so?
MyComponent.jsx :
let myState;
let setMyState;
const MyComponent = () => {
const [myState, setMyState] = useState(35);
return (
<div> Some Jsx </div>
)
}
export myState
export setMyState
ComponentToShareState.jsx :
import myState from MyComponent.jsx
import setMyState from MyComponent.jsx
const MyComponentToShareState = () => {
const onClick = () => {
setMyState(100);
}
return (
<div>
<button onClick=(onClick)> Change my State </button>
<p>{myState}</p>
</div>)
}
Basically is there a reason that prop drilling exists rather than just exporting and importing state from component to component? Is it just for cleanliness of unidirectional flow? Would is actually break the app? Can you explain why this is not a used practice to me?

NextJS: Context API for storing and passing values

I am building this project on NextJS where I want to build a shopping cart. For that, I decided to use useContext hook.
So basically what I did is pretty basic. Create a ShoppingCartContext, add a addToCart function which will push the data to store. But when I press the add to cart button it shows me that,
store.push is not a function.
edit: When I press the button first time, nothing happens. When I press it the 2nd time it gives the mentioned error.
I think I did some basic stuff wrong in the code which is why I am getting this error.
My context code
import {
createContext,
useCallback,
useContext,
useState,
useEffect,
} from "react";
const ShoppingCartContext = createContext();
export function ShoppingCartContextProvider({ children }) {
const [store, setStore] = useState([]);
return (
<ShoppingCartContext.Provider
value={{
store,
setStore,
}}
>
{children}
</ShoppingCartContext.Provider>
);
}
export function useShoppingCartContext() {
const { store, setStore } = useContext(ShoppingCartContext);
//function to push data to array
const addToCart = (product) => {
setStore(store.push(product));
};
return {
store,
setStore,
addToCart,
};
}
Where I called the button component to add data
import { useRouter } from "next/router";
import { useState } from "react";
import { useShoppingCartContext } from "../../context/CartContext";
const SingleProduct = ({ product, categories }) => {
const { store, setStore, addToCart } = useShoppingCartContext();
const router = useRouter();
const breadcrumb = router.components["/shop/[[...category]]"].resolvedAs;
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count - 1);
};
return (
<>
<Button
onClick={() => {
addToCart(product); // product is basically an object which holds all the info of the single product
}}
>
Add to Cart
</Button>
</>
)
}
my _app.js file
import "../styles/globals.css";
import { ChakraProvider } from "#chakra-ui/react";
import { ShoppingCartContextProvider } from "../context/CartContext";
function MyApp({ Component, pageProps }) {
return (
<ChakraProvider>
<ShoppingCartContextProvider>
<Component {...pageProps} />
</ShoppingCartContextProvider>
</ChakraProvider>
);
}
export default MyApp;
please take a look at this.
For updating state which is array you need to do
setStore(store => [...store, product])
//OR
// make clone of store and then setStore
temp=[...store]
temp.push(product)
setStore(temp)
EXPLANATION OF CURRENT BEHAVIOUR
In your case it's working 1st time as you have initialized store as [] empty array.
And because it's an array it has .push() method.
But [].push(val) will not return an array it will return the length of array.
And therefore after 1st time store get's the value of numeric datatype which does not has .push() method.
Below you can see the result of .push() your self.
arr=[1,2,3] // length 3
console.log(`initial array`)
console.log(arr)
console.log(`spread operator + new val`)
console.log([...arr,5]) // spread operator + new val
console.log(`.push()`)
console.log(arr.push(5),"length=",arr.length) // .push() this will return length of the arr

On click returns null instead of an object

It's really basic I guess. I'm trying to add onClick callback to my script & I believe I'm missing a value that would be responsible for finding the actual item.
Main script
import React from 'react';
import { CSVLink } from 'react-csv';
import { data } from 'constants/data';
import GetAppIcon from '#material-ui/icons/GetApp';
import PropTypes from 'prop-types';
const handleClick = (callback) => {
callback(callback);
};
const DownloadData = (props) => {
const { callback } = props;
return (
<>
<CSVLink
data={data}
onClick={() => handleClick(callback)}
>
<GetAppIcon />
</CSVLink>
</>
);
};
DownloadData.propTypes = {
callback: PropTypes.func.isRequired,
};
export default DownloadData;
Storybook code
import React from 'react';
import DownloadData from 'common/components/DownloadData';
import { data } from 'constants/data';
import { action } from '#storybook/addon-actions';
export default {
title: 'DownloadData',
component: DownloadData,
};
export const download = () => (
<DownloadData
data={data}
callback={action('icon-clicked')}
/>
);
So right now with this code on click in the storybook I'd get null and I'm looking for an object.
One of the potential issues I can see is that your handleClick function is stored as it is in-memory, when you import the component. That means you're keeping reference of something that doesn't exists and expects to use it when rendering the component with the callback prop.
Each instance of a component should have its own function. To fix it, move the function declaration inside the component. Like this:
const Foo = ({ callback }) => {
// handleClick needs to be inside here
const handleClick = callback => {
console.log("clicked");
callback(callback);
};
return <div onClick={() => handleClick(callback)}>Click me!</div>;
};
Check this example.
If this doesn't fix your problem, then there is something wrong with how you're implementing Storybook. Like a missing context.

Resources