How to pass data of a component into another component?{ReactJS} - javascript

I have two files Sidebar and UserDataElements.
I want to display data of UserDataElements into Sidebar
I have tried this
This is the main file where i am fetching both the files.
<Sidebar>
<UserDataElements></UserDataElements>
</Sidebar>
Sidebar.js
import React from "react";
import SidebarElements from "./SidebarElements";
const Sidebar = () => { return (
<div className="sideBar">
<SidebarElements></SidebarElements>
</div> ); };
export default Sidebar;
UserDataElements.js
import React from "react";
import userData from "./userData";
const UserDataElements = () => {
return (
<div>
UserData
<ul className="user-ul">
{userData.map((val, key) => {
return (
<li
key={key}
onClick={() => {
window.location.pathname = val.link;
}}
className="userDataList"
>
<div className="d-flex ">
<div id="name">{val.name}</div>
<div id="branch">{val.branch}</div>
</div>
</li>
);
})}
</ul>
</div>
);
};
export default UserDataElements;

You use props for that, which is just like an attribute in HTML, for example if you want to pass data from parent to child you can do it like this
<Sidebar>
<UserDataElements data1={"some_data"} data2={"another_data"}>
</UserDataElements>
</Sidebar>
And in UserDataElements you can access it using props
const UserDataElements = ({ data1, data2 }) => {
// Here data1 and data2 will contain the data you have sent from parent to child
....
}
Or let's say, you want to pass data from child to parent, perhaps on click or something, then you can do it like this
import React from "react";
import userData from "./userData";
const UserDataElements = ({ data1, data2, onItemClick }) => {
return (
<div>
UserData
<ul className="user-ul">
{userData.map((val, key) => {
return (
<li
key={key}
onClick={() => onItemClick && onItemClick(val, key)}
className="userDataList"
>
<div className="d-flex ">
<div id="name">{val.name}</div>
<div id="branch">{val.branch}</div>
</div>
</li>
);
})}
</ul>
</div>
);
};
export default UserDataElements;
Note this specific line
onClick={() => onItemClick && onItemClick(val, key)}
Here we are invoking parent callback method, but before that we check if it exist, and In parent component we can access it like
import React from "react";
import SidebarElements from "./SidebarElements";
const Sidebar = () => {
return (
<div className="sideBar">
<SidebarElements
onItemClick={(val, key) => {
// here you get access to data of clicked element from child to parent
}}
>
</SidebarElements>
</div>
);
};
export default Sidebar;
You should read more about component and props https://reactjs.org/docs/components-and-props.html

Related

React-Router - How to show data passed from one component to another using useNavigate or Link and useLocation

I'm basically trying to show some data in one of my components. The data is passed from my main page but I can't get it to work using useLocation.
I'm getting the data from my firebase db.
My main page is a job board and I want users to be able to click on the job card and go to a new page with all the details of that job.
I see on the console that I get the data but I can't seem to display it in my component/page. I get undefined when doing console.log
See below for more details:
Jobs.js
import { Link, useNavigate } from "react-router-dom";
export default () => {
const navigate = useNavigate();
const [jobs, setJobs] = useState([]);
return (
<div>
{jobs.map(job => {
return (
<div key={job.id}>
//My attempt using Link
<Link
to={`/view-contact-details/${job.id}`}
state={{jobs}}
>
<button>View</button>
</Link>
//My attempt using useNavigate
<button onClick={() => {
navigate(`/view-contact-details/${job.id}`, { state:{ jobs } });
}}
>
Go To Job details
</button>
</div>
);
})}
</div>
);
};
App.js
import React from "react";
import Jobs from "./pages/jobs"
import Form from "./pages/form"
import { Routes, Route } from "react-router-dom"
import ViewUserDetails from "./components/Job/ViewJobDetails";
export default () => {
return (
<div className="App">
<Routes>
<Route exact path='/' element={<Jobs/>} />
<Route exact path='/form' element={<Form/>} />
<Route
exact
path="/view-contact-details/:id"
element={<ViewJobDetails/>}
/>
</Routes>
</div>
);
};
ViewJobDetails.js
import React from "react";
import { useLocation, } from "react-router-dom";
export default (props) => {
const location = useLocation();
console.log(location); // shows jobs on the page yet can't display
//I also tried
//const {state} = location
//{state.job.description}
return (
<>
<div>
<div>
<div>
<strong>Description:</strong>
{location.state.job.description} //doesn't work
{location.job.description} //doesn't work
{location.state.description} //doesn't work
</div>
<div>
<strong>Title:</strong>
</div>
</div>
</div>
</>
);
};
console.log output
The passed state jobs is an array. In the component you are accessing a job property that is undefined.
Access the correct state.jobs array and map it to JSX.
Example:
export default (props) => {
const { state } = useLocation();
const { jobs } = state || []; // <-- access state.jobs
return (
<div>
{jobs.map(job => ( // <-- map jobs array
<React.Fragment key={job.id}>
<div>
<strong>Description:</strong>
{job.description}
</div>
<div>
<strong>Title:</strong>
{job.title}
</div>
</React.Fragment>
))}
</div>
);
};
If on the offhand chance you meant to pass only a single job, i.e. the current job from Jobs, then instead of passing the entire array, pass only the currently iterated job object.
export default () => {
const navigate = useNavigate();
const [jobs, setJobs] = useState([]);
return (
<div>
{jobs.map(job => {
return (
<div key={job.id}>
//My attempt using Link
<Link
to={`/view-contact-details/${job.id}`}
state={{ job }} // <-- pass current job
>
<button>View</button>
</Link>
//My attempt using useNavigate
<button
onClick={() => {
navigate(
`/view-contact-details/${job.id}`,
{ state:{ job } } // <-- pass current job
);
}}
>
Go To Job details
</button>
</div>
);
})}
</div>
);
};
Then in the JobDetals component access location.state.job.
export default (props) => {
const { state } = useLocation();
const { job } = state || {};
return (
<>
<div>
<div>
<div>
<strong>Description:</strong>
{job.description}
</div>
<div>
<strong>Title:</strong>
{job.title}
</div>
</div>
</div>
</>
);
};

599Item.js:12 Uncaught TypeError: product.map is not a function in react

I'm putting together my first react as an exercise to understand but I get this error
599Item.js:12 Uncaught TypeError: product.map is not a function in component item.js on line 12
I copy the component item,js, it has a prop that is the product object
import React, {useState, useEffect} from "react";
import '../App.css';
import 'materialize-css/dist/css/materialize.css';
import Count from './ItemCount';
export const Item =(({product}) => {
const [sellProduct, setSellProduct] = useState(product);
useEffect(() => {
setSellProduct({product});
}, [product]);
const mensaje = () => {
alert ('Gracias por su compra');
}
return (
<>
{
sellProduct.map((item) => {
return(
<Count stock={item.stock} onAdd ={mensaje} >
<div id= {item.id}>
<h3>{item.name}</h3> - <small>{item.category}</small>
<img src= {item.picture} alt="Producto" className="itemImg" />
<p>{item.description}</p>
<p>{item.price}</p>
</div>
</Count>
)
})
}
</>
)
});
expor default Item;
I take the prop out in a fetch and pass it to the child component, which passes the prop to the items component and I copy the code of both
ItemListContainer(parent)
import React, { useEffect, useState } from "react";
import '../App.css';
import 'materialize-css/dist/css/materialize.css';
import ItemList from './ItemList';
export const ItemListContainer =() => {
const [swSell,setSwSell] = useState([])
useEffect(() => {
fetch('https://fakestoreapi.com/products')
.then((res) => res.json())
.then((data) => {
setSwSell(data);
})
.catch((err) => {
console.log(err);
});
}, []);
return(
<div className="parent">
{
swSell.map((item) =>(
<div className="child" key={item.id}>
<ItemList item={swSell} />
</div>
))
}
</div>
)}
export default ItemListContainer;
ItemList(child), this passes to the Item component
import React from "react";
import '../App.css';
import 'materialize-css/dist/css/materialize.css';
import Item from './Item';
export const ItemList = (({item}) => {
return (
<div className="child">
{
item.map((item) =>(
<Item product= {item} />
))
}
</div>
)
});
export default ItemList;
I appreciate your help
In Item component instead of
product.map((item)...
replace it with
product && product.map((item)...
The error:
product.map is not a function in react
arise since the product props that you set as sellProduct is not an array. It is actually an item (object) of swSell array.
So, first, change this:
const ComponentName = (({ ... }) => {
...
})
into this:
const ComponentName = ({ ... }) => {
...
}
Next, since the swSell state is a collection of item object where the item is the product itself, then you only need two component here: ItemList as parent and Item as the child that render the item details
const Item = ({item}) => {
return (
<div id= {item.id}>
<h3>{item.name}</h3> - <small>{item.category}</small>
<img src={item.image} width="100"/>
<p>{item.description}</p>
<p>{item.price}</p>
</div>
)
};
function ItemList() {
const [swSell,setSwSell] = React.useState([])
React.useEffect(() => {
fetch('https://fakestoreapi.com/products')
.then((res) => res.json())
.then((data) => {
setSwSell(data);
})
.catch((err) => {
console.log(err);
});
}, []);
return (
<div>
{swSell.map((item) => (
<div className="child" key={item.id}>
<Item item={item} />
</div>
))}
</div>
);
}
ReactDOM.render(<ItemList />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>

getBoundingClientRect is reading a null value

I am getting the error shown in the title, and don't know why. Basically I want to get back in the console log a set a values including the height of the links.
TypeError: Cannot read property 'getBoundingClientRect' of null
Why is that. Does it come from the useRef(null) assigned on the linksRef variable?
Here is the component!
import React, { useState, useRef, useEffect } from 'react'
import { FaBars, FaTwitter } from 'react-icons/fa'
import { links, social } from './data'
import logo from './logo.svg'
const Navbar = () => {
const [showLinks, setShowLinks] = useState(false)
const linksContainerRef = useRef(null)
const linksRef = useRef(null)
useEffect(() => {
// We' ll use the height of the links to
// adjust the height of the list container
const linkHeight = linksRef.current.getBoundingClientRect()
}, [showLinks])
return (
<nav>
<div className='nav-center'>
<div className='nav-header'>
<img src={logo} alt='logo' />
<button
className='nav-toggle'
onClick={() => {
setShowLinks(!showLinks)
}}
>
<FaBars />
</button>
</div>
{showLinks && (
<div
className='links-container'
ref={linksContainerRef}
// className={`${
// showLinks ? 'links-container show-container' : 'links-container'
// }`}
>
<ul className='links' ref={linksRef}>
{links.map((link) => {
const { id, url, text } = link
return (
<li key={id}>
<a href={url}>{text}</a>
</li>
)
})}
</ul>
</div>
)}
<ul className='social-icons'>
{social.map((socialIcon) => {
const { id, url, icon } = socialIcon
return (
<li key={id}>
<a href={url}>{icon}</a>
</li>
)
})}
</ul>
</div>
</nav>
)
}
export default Navbar
Thanks,
Theo.
Yes, do a not null check before accessing anything on a ref.current.

How to render multiple components with different component name?

I have a few components, they have the same parameter with iterative values, like this:
import React from "react";
import Panel from "./Panel";
import Navbar from "./Navbar";
export default function App() {
return (
<div className="App">
<Panel id={1} />
<Navbar id={2} />
</div>
);
}
const Panel = ({ id }) => {
return (
<div>The id is {id}</div>
);
};
const Navbar = ({ id }) => {
return (
<div>The id is {id}</div>
);
};
Working example here: https://codesandbox.io/s/staging-pond-mpnnp
Now I'd like to use map to render those components at once in App.js, something like this:
export default function App() {
const compnentArray = ['Panel', 'Navbar'];
const RenderComponents = () => {
let _o = [];
return (
componentArray.map((item, index) => _o.push(<{item} id={index} />))
)
}
return (
<div className="App">
{RenderComponents()}
</div>
);
}
So that item renders component names. Is this possible?
Sure, you could make use of Array.map()'s second parameter which gives you the index in the array:
import React from "react";
import Panel from "./Panel";
import Navbar from "./Navbar";
const components = [Panel, Navbar];
export default function App() {
return (
<div className="App">
{components.map((Component, i) => (
<Component key={i} id={i + 1} />
))}
</div>
);
}
As mentioned in React's documentation, to render a component dynamically, just make sure you assign it to a variable with a capital first letter and use it like you'd use any other component.
You could swap strings with your actual component references and itererate over them directly in your JSX part, like this :
export default function App() {
const componentsArray = [Panel, Navbar];
return (
<div className="App">
{componentsArray.map((Component, index) => <Component key={index} id={index + 1} />)}
</div>
);
}
Though I would suggest to memoize them to improve performance once you're confortable enough with React to start using memoization.
import React from "react";
import Panel from "./Panel";
import Navbar from "./Navbar";
const components = [Panel, Navbar]; // notice you are using the components as items, not strings;
/*
if the components need props from the parent,
the `renderComponents()` function should be declared
inside the parent component (and possibly with a `useCallback()`
hook, to avoid unnecessary re-declarations on re-renders)
*/
function renderComponents() {
return components.map((comp, index) => <comp key={index} id={index} />) || null;
}
export default function App() {
return (
<div className="App">
{renderComponents()}
</div>
);
}

How to create a simple list maker app in React.JS?

I'm working on a simple list maker, to do list app using create-react-app and I'm having some trouble puzzling out the functionality. What I'm trying to accomplish with this app:
I want to be able to enter text into an input, push the button or press enter, and whatever text will be listed on the body of the app.
I want to be able to create a button that will delete the list items once the task or objective is complete
My code is broken up into these components so far:
App,
ListInput,
ItemList,
Item
The code for App is
import React, { Component } from 'react';
import './App.css';
import Navigation from './components/Navigation';
import ListInput from './components/ListInput';
import ListName from './components/ListName';
import Item from './components/Item';
import ItemList from './components/ItemList';
class App extends Component {
constructor() {
super();
this.state = {
input: '',
items: []
};
}
addItem = () => {
this.setState(state => {
let inputValue = this.input.current.value;
if (inputValue !== '') {
this.setState({
items: [this.state.items, inputValue]
})
}
})
}
onButtonEnter = () => {
this.addItem();
}
render() {
return (
<div className="App">
<Navigation />
<ListName />
<ListInput addItem={this.addItem}
onButtonEnter={this.onButtonEnter} />
<Item />
<ItemList />
</div>
);
}
}
export default App;
The code for ListInput is :
import React from 'react';
import './ListInput.css';
const ListInput = ({ addItem, onButtonEnter }) => {
return (
<div>
<p className='center f2'>
{'Enter List Item'}
</p>
<div className='center'>
<div className='center f3 br-6 shadow-5 pa3 '>
<input type='text'
className='f4 pa2 w-70 center'
placeholder='Enter Here'
/>
<button className='w-30 grow f4 link ph3 pv2 dib white bg-black'
onClick={onButtonEnter}
onSubmit={addItem} >
{'Enter'}
</button>
</div>
</div>
</div>
);
}
export default ListInput;
The code for Item is:
import React from 'react';
const Item = ({text}) =>{
return (
<div>
<ul>{text}</ul>
</div>
)}
export default Item;
And the code for ItemList is :
import React from 'react';
import Item from './Item';
const ItemList = ({ items }) => {
return (
<div>
{item.map(items => <Item key={item.id}
text={item.text} />
)}
</div>
)
}
export default ItemList;
In my react app I am returning an error of 'item' is not defined and I'm confused why.
In your App.js you need to pass items as a prop to ItemList component like
<ItemList items={this.state.items} />
Also in addItem function pushing inputValue to items array isn’t correct do something like below
addItem = () => {
this.setState(state => {
let inputValue = this.input.current.value;
if (inputValue !== '') {
this.setState(prevState => ({
items: [...prevState.items, inputValue]
}));
}
})
}
And in ItemList.js do conditional check before doing .map also some typo errors in .map
import React from 'react';
import Item from './Item';
const ItemList = ({ items }) => {
return (
<div>
{items && items.map(item => <Item key={item.id}
text={item.text} />
)}
</div>
)
}
export default ItemList;
Try with above changes This would work
Please excuse me if there are any typo errors because I am answering from my mobile
Your ItemList was not correct. Take a look at corrected snippet below you need to map on items and not item (hence the error item is not defined). Also, you need to items as a prop to ItemList in your app.js
import React from 'react';
import Item from './Item';
const ItemList = ({ items }) => {
return (
<div>
{items.map(item => <Item key={item.id}
text={item.text} />
)}
</div>
)
}
export default ItemList;
In app.js add following line. Also, I don't see what is doing in your app.js remove it.
<ItemList items={this.state.items}/>
Seems like you have a typo in ItemList.
It receives items (plural) as prop but you are using item.
const ItemList = ({ items }) => {
return (
<div>
{items.map(items => <Item key={item.id}
text={item.text} />
)}
</div>
)
}
And don't forget to actually pass the items prop to 'ItemList':
<ItemList items={this.state.items} />

Categories

Resources