As indicated, I was trying to pass down an image link from the internet.
When I do this, the code does not give an error, but fails to load the image:
Parent component:
function App() {
return (
<div className="App">
<Header imgLink="https://i.ibb.co/Q6q5F3x/profile.png" />
</div>
);
}
Child component:
const Header = (imgLink) => {
return(
<div className="header">
<img className="profile-pic" src={imgLink} alt="Profile Pic"/>
</div>
);
}
When I simply use the image link as the img src without it being passed down from the parent component, it would work; but I would still like it to be passed down from the parent.
Any insight will help! Thanks
You need to destruct params in Header component.
const Header = ({imgLink}) =>
else it's just an props object:
const Header = (props) => {console.log(props.imgLink)}
Related
I have a component which has child components, i want to render these child components with different Ids. They are getting their data from store.The problem is they are rendered but with the same item. how can this be solved?
MultiImages Component
const MultiImages: () => JSX.Element = () => {
const values = ['500', '406', '614'];
return (
<div>
{values.map((val, index) => {
return <OneImage key={index} projectID={val} />;
})}
</div>
);
};
export default MultiImages;
OneImage Component
const OneImage: () => JSX.Element = ({ projectID }) => {
const projectData = useProjectDataStore();
const { getProject } = useAction();
useEffect(() => {
getProject(projectID ?? '');
}, []);
return (
<>
<div>
<img
src={projectData.picture}
}
/>
<div>
<a>
{projectData.projectName}
</a>
</div>
</div>
</>
);
};
export default OneImage;
Your issue here - you are calling in a loop, one by one fetch your projects, and each call, as far as we can understand from your example and comments override each other.
Your are doing it implicitly, cause your fetching functionality is inside your Item Component OneImage
In general, the way you are using global state and trying to isolate one from another nodes is nice, you need to think about your selector hook.
I suggest you, to prevent rewriting too many parts of the code, to change a bit your selector "useProjectDataStore" and make it depended on "projectID".
Each load of next project with getProject might store into your global state result, but instead of overriding ALL the state object, you might want to use Map(Dictionary) as a data structure, and write a result there and use projectID as a key.
So, in your code the only place what might be change is OneImage component
const OneImage: () => JSX.Element = ({ projectID }) => {
// making your hook depended on **projectID**
const projectData = useProjectDataStore(projectID);
const { getProject } = useAction();
useEffect(() => {
// No need of usage **projectID** cause it will inherit if from useProjectDataStore
getProject();
}, []);
return (
<>
<div>
<img
src={projectData.picture}
}
/>
<div>
<a>
{projectData.projectName}
</a>
</div>
</div>
</>
);
};
export default OneImage;
And inside of your useProjectDataStore store result into a specific key using projectID.
Your component OneImage will return what's in the return statement, in your case:
<>
<div>
<img
src={projectData.picture}
/>
<div>
<a>
{projectData.projectName}
</a>
</div>
</div>
</>
This tag <></> around your element is a React.fragment and has no key. This is the reason you get this error.
Since you already have a div tag wrapping your element you can do this:
<div key={parseInt(projectID)}>
<img
src={projectData.picture}
/>
<div>
<a>
{projectData.projectName}
</a>
</div>
</div>
You can also change the key to Math.floor(Math.random() * 9999).
Note that passing the prop key={index} is unnecessary, and is not advised to use index as keys in a react list.
I am creating an application in react that takes output from a mongodb database. In one of the Gallery components, I have placed a map function that displays a list of items. I would like to add an onClick event to each element of the array, which would pass each property to the other component as state.
I have looked for hints in other posts, but none fit my case. I have also tried to solve it myself as far as my skills with react allow me.
What I would like is to pass the individual properties i.e.: file.name and file.cover to the other component.
Gallery Component:
function Gallery() {
const [name, setName] = useState()
const [cover, setCover] = useState()
const video = [...]
const seeDetails = () => {
setName(???)
}
return(
<section>
<ul className='grid grid-cols-2'>
{video.map((file) => (
<li key={file.name} className='relative' onClick={seeDetails}>
<div>
<img
src={file.cover}
alt={file.name}
/>
</div>
<p>
{file.name}
</p>
</li>
))}
</ul>
</section>
<SecondComponent name={name} setName={setName} cover={cover} setCover={setCover} />
)}
SecondComponent:
const SecondComponent = ({ name, setName, cover, setCover }) => {
return (
<p>{name}</p>
<img src={cover} alt={name} />
)
}
seeDetails function doesn't seem to have any provision to be able to access your file object. It be able to accept the file object as a parameter, which I can see from your code snip, contains everything you need to share with SecondComponent
Try:
const seeDetails = (file) => {
setName(file.name);
setCover(file.cover)
}
and you will need it to be explicitly passed through the onClick prop as
<li key={file.name} className='relative' onClick={(event) => seeDetails(file)}>
Also, if it is feasible, instead of creating individual states for name, cover, xyz; would be good to have the file object itself maintained in the state.
i'm learning the react techologie, i'm the first exercice i must put the images like this, but when i use the map function to put the images i don't saw the images :/
image dynamics example
import React from 'react'<br>
import './Style/App.css'
const tabImg = ['./img/html.png', './img/css.png', './img/javascript.png', './img/logo192.png']<br>
const displayImgTech = tabImg.map((tech) => <img key={tech} src={tech} alt="techno"/>)<br>
export const Header = () => {
return (
<div className='container'>
<img src={displayImgTech} alt='technoFE'/>
</div>
)
}
Thank you
By passing displayImgTechin the src img props your are not adding an url but an "array" of img tags. so you just need to call {display take in your div like this:
const displayImgTech = tabImg.map((tech) => <img key={tech} src={tech} alt="techno"/>)<br>
export const Header = () => {
return (
<div className='container'>
{displayImgTech}
</div>
)
}
I have 2 components list of posts and when clicking on link on post card i'm entering into post.
I can't access props.postDetails in child component. When I console log the props, I have {history: {…}, location: {…}, match: {…}, staticContext: undefined} only this without props.postDetails.
Can somebody help?
Code for parent component is:
mport {useState, useEffect} from 'react';
import {BrowserRouter as Router, Switch, Route, Link, withRouter} from "react-router-dom";
import logo from "./assets/images/logo.jpg";
import Post from './Post';
const Home = () => {
const [posts, setPosts] = useState([]);
useEffect(() => {
getResults();
},[]);
const getResults =() => {
fetch("https://blog-d8b04-default-rtdb.europe-west1.firebasedatabase.app/posts.json")
.then(response => response.json())
.then(data => {setPosts(data)});
}
const postsArr = [];
Object.values(posts).forEach((post, key) => {
postsArr.push(post);
});
return(
<div>
<div className="container-fluid">
<div className="row">
<div className="posts-container col-md-12">
<div className="row">
{
postsArr.map((post, key) => (
<div className="col-md-4">
<Link to={`/post/${key}`} >
<div className="pic-wrapper">
<img className="img-fluid" src={post.pic} alt={post.title}/>
</div>
<h4>{post.title}</h4>
<Post postDetails={post}/>
</Link>
</div>
))
}
</div>
</div>
</div>
</div>
</div>
)
}
Code for child component:
import {withRouter} from "react-router-dom";
const Post = (props) => {
const {pic, title, author, description} = props.postDetails;
return(
<div className="container">
<div className="pic-wrapper">
<img className="img-fluid" src={pic} alt={title}/>
</div>
<h4>{title}</h4>
<p>{author}</p>
</div>
)
}
export default withRouter(Post);
Issue
Ok, it's as I started to suspect. You are rendering a Post component in more than 1 place.
The issue here is that in Home.js you are passing a postDetails prop, (<Post postDetails={post.pic} />), but in app.js you are only passing the route props from Route, (<Route path="/post/:postId" exact strict component={Post} />). This Post component is the one triggering the error.
Solution
An easy solution is to simply pass the post data along with the route transition.
<Link
to={{
pathname: `/post/${key}`,
state: {
post
}
}}
>
...
<Post postDetails={post.pic} />
</Link>
And access the route state on the receiving end in Post. Try to read the post details from props first, and if they is falsey (null or undefined) assume it was passed in route state and access it there.
const Post = (props) => {
const { state } = props.location;
const { pic, title, author, description } = props.postDetails ?? state.post;
return (
<div className="container">
<div className="pic-wrapper">
<img className="img-fluid" src={pic} alt={title} />
</div>
<h4>{title}</h4>
<p>{author}</p>
</div>
);
};
Of course there is room to make this a bit more robust but this is a good start.
Additional Suggestion
Instead of saving post state that isn't formed correctly for what/how you want to render it, you can transform the response data before saving it into state. This save the unnecessary step of transforming it every time the component rerenders.
const getResults = () => {
setLoading(true);
fetch(
"https://blog-d8b04-default-rtdb.europe-west1.firebasedatabase.app/posts.json"
)
.then((response) => response.json())
.then((data) => {
setPosts(Object.values(data));
setLoading(false);
});
};
Then map as per usual. Make sure to place the React key on the outer-most mapped element, the div in your case.
{posts.map((post, key) => (
<div className="col-md-4" key={key}>
...
</div>
))}
Demo
That is indeed an expected behaviour, because you are actually mapping what appears to be an empty array - see postArr; on your first render it will result as an empty array and since that's not a state, it will never re render your child component with the appropriate props.
I don't really see why you fetch the data, set them to your posts useState and then copy them over to a normal variable; Instead, remove your postArr and on the map replace it with your posts directly.
Since that's a state, react will listen to changes and rerender accordingly, fixing your problem
Im looking into higher order functions and i dont really understand how this part works.
say i have the following function:
const withAdminWarning = WrappedComponent => {
return props => (
<div>
{props.isAdmin && <p>This is private info. Please dont share!</p>}
<WrappedComponent {...props} />
</div>
);
};
const Info = props => (
<div>
<h1>Info</h1>
<p>This info is: {props.info}</p>
</div>
);
const AdminInfo = withAdminWarning(Info);
ReactDOM.render(
<AdminInfo isAdmin={true} info="There are the details" />,
document.getElementById("app")
);
From my understanding of components, to access the props variable, you have to use either props, if its a stateless component, or this.props if it is a class component.
From where does the props come into play in the example above as i cant get access to it from the WrappedComponent or anywhere else apart from the return statement.
The Higher order Component returns a function which is a functional component. Am I right in thinking that foo(Info) means withAdminWarning(Info)?
So after calling withAdminInfo the AdminInfo Component looks basically like:
const AdminInfo = props => (
<div>
{props.isAdmin && <p>This is private info. Please dont share!</p>}
<div>
<h1>Info</h1>
<p>This info is: {props.info}</p>
</div>
</div>
);