I'm new to react and I'm trying to figure out the best way to make a component that can handle different scenarios. I'm not sure if the best practice would be to make multiple components or one component to handle it all.
Imagine a frontpage were you have 3 different entrances like recent products, blogpost or Instagram pictured. Each entrance use a component called featured and inside that component I should render either products, blogpost or Instagram pictures. Everything for the layout is the same, its just the items in the grid that needs to change. What would be the best way to solve this? one component with 3 different sub-components or 3 components with one for each type.
I know how to make 3 different components, but I'm not sure how to make one component to handle subcomponents.
This could be the component and the "grid-item--product" could also be a "grid-item--blogpost or "grid-item--Instagram" - "grid" could also be a "two-col" or "three-col".
<div className="featured">
<div className="featured--content">
<div className="grid four-col">
<grid-item--product />
</div>
</div>
</div>
and this could be where I would call the component and hopefully be able to handle which component should be rendered inside and what the grid should be for this feature.
<div className="frontpage-route">
<h2>Frontpage Route</h2>
<Featured />
</div>
Can you help me? I would love an example if possible.
Thanks.
It sounds like what you want is the children prop. You can add the children prop to Featured and just pass the correct children to it. See an example here:
const Featured = ({ children, numColumns = "one" }) => (
<div className="featured">
<div className="featured--content">
<div className={`grid ${numColumns}-col`}>
{children}
</div>
</div>
</div>
)
const App = () => (
<div className="frontpage-route">
<h1>Frontpage Route</h1>
<h2>Products</h2>
<Featured numColumns="two">
<grid-item--product />
<grid-item--product />
</Featured>
<h2>Blogs</h2>
<Featured numColumns="three">
<grid-item--blog />
<grid-item--blog />
</Featured>
<h2>Instagram</h2>
<Featured>
<grid-item--instagram />
<grid-item--instagram />
</Featured>
</div>
)
You can use consitional rendering and three boolean variables to display components.
e.g:
<div className="featured">
<div className="featured--content">
<div className="grid four-col">
{product && <grid-item--product />} //if product var is true this component renders
{blogpost && <grid-item--blogpost />} //if blogpost var is true this component renders
{instagram && <grid-item--instagram />} //if instagram var is true this component renders
</div>
</div>
</div>
Related
TLDR: Cannot figure out why component is still being rendered while no props are passed.
So I have been building a NextJS application, and I have this banner component that is shown on every page of my website. It has some header text, buttons and an image:
const Banner = (props) => {
return (
<div className={bannerStyles.wrapper}>
<div className={classnames(bannerStyles.banner, "wrap", "center")}>
<div className={bannerStyles.banner_left}>
<h1>{props.header}</h1>
<div className={bannerStyles.button_wrapper}>
<div className={bannerStyles.button}>
<Button>{props.button || null}</Button>
</div>
<div className={bannerStyles.button}>
<Button>{props.scnd_button || null}</Button>
</div>
</div>
</div>
<div className={bannerStyles.banner_right}>
<Image src={props.image} alt=""></Image>
</div>
</div>
</div>
);
};
Inside of this, as you can see I have two Button components (The MDEast thing is an arrow icon):
const Button = ({children}) => {
return (
<div className={buttonStyles.button}>
<Link href="/"><a>{children} <MdEast /></a></Link>
</div>
)
}
Now I want the option that if no prop is passed, that the Button component(s) do(es) not render/ is hidden from the page, so that it is optional per page. Yet the Button does still render, even though I am not passing any props on my About page. My about page:
const About = () => {
return (
<>
<Banner
header="Hello this is my code"
image={banner_placeholder}
/>
</>
)
}
PS. I am fairly new to React and NextJS, so this might be a beginner mistake, or I am not understanding the fundamentals well enough, but could someone point me in the right direction please?
To conditionally render the button you can use:
props.button && <Button>{props.button}</Button>
When props.button is falsy, then button will not get rendered.
i am trying tab in next in next.js, but every time i use it it show a console warning link this Prop `id` did not match. Server: "react-tabs-30" Client: "react-tabs-0", i know it isn't effect my app but it is so annoying. how to solve this waring
<Tabs>
<div className="tab-controler ml-sm-auto">
<TabList className="tab-lists list-inline d-flex flex-wrap nav mb-3" style={{ background: '#F8F8F8' }}>
<Tab className={`${CostCalculatorStyle.PEItem} tab-lists__item`}>Buy & Ship for me</Tab>
<Tab className={`${CostCalculatorStyle.PEItem} tab-lists__item`}>Ship for me</Tab>
</TabList>
</div>
<TabPanel key={"tabpanel_ship"}>
<div className="row">
<div className="col-lg-6">
<ShipForMeForm handleFormValue={handleFormValue} handleProductValue={handleProducts} handleRef={handleRef} />
</div>
<div className="col-lg-6 align-self-center">
<div className="costcalc-empty-thumb text-center">
<Image
src="/assets/topNavbarPages/costCalculator.svg"
alt="Cost Calculator"
width="469"
height="288"
/>
</div>
</div>
</div>
</TabPanel>
<TabPanel key={"tabpanel_buy_ship"}>
<div className="row">
<div className="col-lg-6">
<ShipForMeForm handleFormValue={handleFormValue} handleProductValue={handleProducts} handleRef={handleRef} />
</div>
<div className="col-lg-6 align-self-center">
<div className="costcalc-empty-thumb text-center">
<Image
src="/assets/topNavbarPages/costCalculator.svg"
alt="Cost Calculator"
width="469"
height="288"
/>
</div>
</div>
</div>
</TabPanel>
</Tabs>
as you can see i use react-tabs for tab but i also work on react js where i use the same code but it didn't show this console warning. so my question is why it is happing? and how i can solve it ?
In next js i fixed it like that
import dynamic from 'next/dynamic'
const Tabs = dynamic(import('react-tabs').then(mod => mod.Tabs), { ssr: false }) // disable ssr
import { Tab, TabList, TabPanel } from 'react-tabs'
it work for me
The best solution I know of is just to set the id yourself in the <Tabs> component. Ex: <Tabs id={1}>
See https://github.com/chakra-ui/chakra-ui/issues/4328#issuecomment-890525420 for more details and examples.
NextJs generating code in server side as you know.
This error means that something on the server is different from the Client. This can happen if the client does a re-render.
For example.
export default function Test(props) {
return (
<>
<span>{props.name}</span>
</>
);
}
I have this simple Test component.And I send name (1) prop from another component to this Test component. And if I change this name (to 2) in client using redux (for example I have another name in my redux store) after page generated I get this error.
props did not match server "1" client "2"
To solve this error I need to just not change this name with redux after page generated in server. The data can be change only with user manipulations after page rendered in server.
Same thing happened to me when I use Tabs from react-bootstrap. Koushik Saha's answer can be apply for that also but with a small change. Need to put react-bootstrap instead react-tabs
const Tabs = dynamic(import('react-bootstrap').then(mod => mod.Tabs), { ssr: false })
In case you miss it
If you are using Nextjs + Material-ui, there are actually custom codes that you can/need to include in your _document.js and _app.js to remove the server-side injected CSS so the CSS is recreated when page loads.
As codes changes with mui's and nextjs' version, i will refer you to the repository directly
https://github.com/mui-org/material-ui/tree/master/examples/nextjs/pages
As per the documentation in the react-tabs repo/npmjs.org page (https://www.npmjs.com/package/react-tabs):
import { resetIdCounter } from 'react-tabs';
resetIdCounter();
ReactDOMServer.renderToString(...);
I was having the same issue and called the resetIdCounter function inside my parent component to the tabs structure and cleared up the error.
Not sure if maybe there is a better place to use this function, like maybe in a useEffect hook or something, but I'm going with this for now.
So far the map is working perfectly except for one problem, every time I click the button to post a comment a whole new post populates the UI. What I want is to only render a comment not a whole post every time I click the post comment button. What I tried to do is do map within a map, as you can see below. However its still rendering the a whole post. How do I map for a certain item, I think that will help. What should I do?
const { TextArea } = Input;
const PostOnWall = (props) => (
<div>
{props.postInfo.map( (item) => (
<div>
<div className="PostOnWall">
<div className="topbar">
<img src = {profile} className="image"/>
<div className="name">Brad Pitt</div>
<div>{item.time}</div>
</div>
<div className="text">{item.post}</div>
<img src={item.uploadedImage} />
</div>
<div className="engagementBar">
<div><FontAwesomeIcon icon={fathumbsup} size="2x"/> Like</div>
<div><FontAwesomeIcon icon={facomment} size="2x"/> Comment</div>
<div><FontAwesomeIcon icon={fasharesquare} size="2x"/> Share</div>
</div>
<div className="postCommentBox">
<img src = {profile} className="image"/>
<TextArea type = "text" placeholder="Write a comment" autoSize id="comment" onChange={props.onChange}/>
<div>
<button onClick={props.onClick}></button>
</div>
</div>
{item.comment.map( (items) => (
<div>
<div> {items} </div>
</div>
))
}
</div>
))
}
</div>
)
I think with current structure it's not really possible to re-render only comments. In order to make that possible you might need to move comments to a separate component.
Another important thing I spot you don't use key while rendering lists, so it decreases performance quite a lot as React will need to re-render the whole list on each render. You can read about it here: https://reactjs.org/docs/lists-and-keys.html
BACKGROUND
I am attempting to learn ReactJS by building my first Single Page Application. I usually work in .NET MVC, and I'm coming across some difficulties in the thought process required for SPAs. Anyways, I am using the latest version of React & React Router. Here is what I have at the moment.
CODE
So far, I have 5 true components in total. Here they are in order of usage:
App
ArtistList
Loops over artist array (coming from a local variable so far) and draws Artistcards
ArtistCard
Artist
Think of this as the detailed view of an artist. You get here by clicking an ArtistCard (shown in screenshot) so that you can see the list of LyricCards
LyricCard
My routes are declared in my [App component]. Here is the JSX for that.
<div className="App">
<Header />
<Route exact path="/" render={routeProps => <ArtistList {...routeProps} artists={artists} />} />
<Route exact path="/artist/:artistUrlName" component={Artist} />
</div>
Here is the code for my [ArtistList component]:
class ArtistList extends Component {
render() {
return (
<div className="artist-list">
{this.props.artists.map(artist =>
<ArtistCard key={artist.imageUrl} artist={artist} history={this.props.history} />
)}
</div>
);
}
}
Here is the code for my [ArtistCard component]:
class ArtistCard extends Component {
constructor() {
super();
this.navigateToArtist = this.navigateToArtist.bind(this);
}
navigateToArtist() {
this.props.history.push(`/artist/${this.props.artist.urlFriendlyName}`);
}
render() {
return (
<div
className="artist-card"
onClick={() => {
this.navigateToArtist();
}}
>
<div className="artist-image">
<img src={this.props.artist.imageUrl} alt="artist" />
</div>
<div className="artist-name">
<h3>
{this.props.artist.name}
</h3>
</div>
<div className="artist-meta">
<div className="artist-lyrics">
<h4>
{this.props.artist.lyricsCount}
</h4>
</div>
<div className="artist-hearts">
<h4>
{this.props.artist.likesCount}
</h4>
</div>
</div>
</div>
);
}
}
PROBLEM
When clicking an artist card (e.g. James Brown), I am able to navigate to the route /artist/james-brown, but I don't know how I can pass the data (the props) required by the [Artist component] (which will show a list of lyric cards).
As mentioned, this is my first ever SPA, and I am not sure if I'm going about this the right way. As a developer, I know I can grab the urlFriendlyName from the URL and pass that to an API endpoint that would get me a list of the lyrics (and other stuff I may need), and I can then set those on the state of the component via one a relevant component life-cycle mount method.
But is that the way with SPAs? I mean another way I can do this would be to make a single call to the API endpoint in my controller / parent [App component] and get all artists and their lyrics and everything else I need? I'll still need you to tell me how I can then pass the artist data and all his/her lyrics via props to the [Artist component].
I have what I thought would be a very simple question about reusing html wrappers for components. But I'm not sure what the 'proper' way to do it is.
I want to render a series of components reusing the same (complex) html wrapper for each of them. Then rendering the a set of different child components inside that wrapper.
(render 10 of these:)
<div i start>
<div start reusable wrapper>
<child component - one of ten different components, one after the other>
<div end reusable wrapper
<div i end>
I just need a high level suggestion. I feel like everything I think of is 'hacking it'.
const Layout = props => (
<div whateverattributes>
<div whateverotherattributes>
{props.children}
</div>
</div>
);
Then
<Layout>
<ChildComponent />
</Layout>