Pass props to props.history.push('path') in ReactJS - reactjs

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].

Related

How do I consume data from a component where I am passing data using Link of react-router in class Components?

I have a carousel of cards. And each card has some data which if I click on the button on the card. I want to render the details of that particular card in different components on a different page which is item-details.
How do I achieve this in class components? There is an id in the map can I send using that in the details page where I am trying to render the full details of the card.
{this.state.data.map((item, idx) => {
return (
<div key={`edth_${idx}`} className="col-12 col-sm-6 col-lg-4 item explore-item" data-groups={item.group}>
<div className="card">
<div className="image-over">
<img className="card-img-top" src={item.img} alt="" />
</div>
<div className="card-caption col-12 p-0">
<div className="card-body">
<a href="/item-details">
<h5 className="mb-0">{item.title}</h5>
</a>
<div className="card-bottom d-flex justify-content-between">
<span>{item.price}</span>
<span>{item.count}</span>
</div>
<Link to={{pathname: '/item-details',state: [{img:'item.img',count: 'item.count', title: 'item.title', price: 'item.price'}]}} className="btn btn-bordered-white btn-smaller mt-3"> <i className="icon-handbag mr-2" />Check it out</Link>
</div>
</div>
</div>
</div>
);
})}
</div>
I have added the Link from react router instead of, but I still don't know how to consume this in the component which will be in /item-details.
Found out that instead of useLocation, I can use with router in class components so...
Imported useRouter in the item-details page and consuming like this in the render():
render() {
const { location } = this.props;
return (
<div className="item-info">
{location && location.img && <div className="item-thumb text-center">
<img src={location.img} />
</div>}
);
By doing this, I am not getting any error. But the img is not showing.
React is generally used for a single paged application (i.e. you wouldn't be sending users to different URLS) unless you are using something like NEXT, React Router, etc. for handling different pages.
Instead of sending the user to another page, you would render a new component with the details sent as a prop.
But if you're asking how to send information from a component to another page, i would suggest you look into React Router, which is designed to do this.
I'm not sure if this is actually what you're asking though so please provide further details on what your project is made up of.
In my experience, the best way to handle this would be to set the URL query when the onClick() handler is run. The view would update & the details would be fetched as needed. That way if you your homepage is mysite.com you can get more details by visiting mysite.com/moreinfo.
To accomplish this, you can use react-router-dom history API:
import { useHistory } from "react-router-dom";
function myComponent() {
let history = useHistory();
function handleClick() {
history.push("/moreinfo");
}
return (
<button type="button" onClick={handleClick}>
View More Info
</button>
);
}

React - Carbon Design System - Transition in right sidebar not working

I'm trying to use Carbon Design System for a project, and I need some help with the UI Shell.
The transition of the right sidebar is not working properly.
Here is the working example, see how smooth it opens and closes (see live example):
Here is the example with issues. There's no transition (see live example):
I think it's related to how High Order Components work. Here's my theory:
HeaderPanel is being rendered inside the HeaderContainer, which is a High Order Component.
When the state variable switchCollapsed changes, the HeaderContainer is rendered entirely again.
So this would explain why there's no transition.
I'm new to React, and this theory is based on my React knowledge until now. Please correct me if I'm wrong
Here's a piece of the code of the second example (simplified for the brevity of this post). See the entire code here
class App extends React.Component {
constructor(props) {
super(props);
this.toggleSwitch = this.toggleSwitch.bind(this);
this.state = { switchCollapsed: true };
}
toggleSwitch() {
this.setState(state => ({
switchCollapsed: !state.switchCollapsed
}));
}
render() {
return (
<div className="container">
<HeaderContainer
render={({ isSideNavExpanded, onClickSideNavExpand }) => (
<>
<Header aria-label="IBM Platform Name">
<HeaderMenuButton
onClick={onClickSideNavExpand}
isActive={isSideNavExpanded}
/>
<HeaderGlobalBar>
<HeaderGlobalAction onClick={this.toggleSwitch}>
<UserAvatar20 />
</HeaderGlobalAction>
</HeaderGlobalBar>
<HeaderPanel
aria-label="Header Panel"
expanded={!this.state.switchCollapsed}
/>
<SideNav
aria-label="Side navigation"
expanded={isSideNavExpanded}
>
<SideNavItems>
</SideNavItems>
</SideNav>
</Header>
<StoryContent />
</>
)}
/>
</div>
);
}
}
render(<App />, document.getElementById("root"));
Moreover, if I change the property on React Dev Tools, the transition work!

Reactjs component - one vs many

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>

Nested subcomponents in React + rendering new data

Help needed please,
I am currently learning react and I ran into an issues of some sort while creating a blog app.
I nested components within each other, however updated data is not received in subcomponent unless I refresh the page of the nested-component. Below is snippet of my codeWithin the AccountOverview component is another component(container) that handles a form. Once the form submits data and the backend processes the data, i expect that the AccountOverview component will render new info. However if i then go back to the parent (dashboard) and make an edit the AccountOverview component does't display newly created data except on a full-page refresh.
class Dashboard extends Component {
state = {}
componentDidMount(){
this.props.getCurrentProfile();
}
render() {
const { user } = this.props.auth;
const { profile, loading } = this.props.profile;
return (
<div style={{margin: "0", padding: "0"}}>
<Header title="Account Dashboard" />
<section id="accountPage" className="">
<div className="container">
<div className="row">
<Sidebar />
<main className="col-sm-9 col-md-9 account">
<div className="dashboard">
<AccountOverviewPanel
user={user} profile={profile} loading={loading}
/>
<PostTablePanel />
</div>
</main>
</div>
</div>
</section>
</div>
);
};
};
Is your submit action casusing your components to unmount.
I tried cloning your code. it seems to miss a file for keys hence couldn't run it

Dynamically style a React element

I am trying to dynamically style a React element: In the nav bar, I would like to make the font color of the <div> displaying the current page to be different from the other <div>s representing other pages. I have passed into my <NavBar> component's props the params representing the current page.
From this information, I have figured out a way to achieve what I am trying to do: I store the <div>s representing the page links in an object and wrap the selected page in another <div> carrying a 'selected'class name.
However, I could envision scenarios where this solution would disrupt styling in place since it adds a wrapper element and classes within the wrapper element would take precedence over the 'selected' class.
Does anyone know a better way? I have posted my solution and the context below:
export default class NavBar extends React.Component {
constructor(props) {
super(props);
}
render() {
const pages = {
my_subscribed_docs:
<div className='nav__row2-item'>Documents I'm Subscribed To</div>,
my_created_docs:
<div className='nav__row2-item'>Documents I've Created</div>,
new_doc:
<div className='nav__row2-item'>
<div className="plus-icon">+</div>
<div className='nav__row2-text_left-of-icon'>New Document</div>
</div>,
};
const currentPage = this.props.path.slice(1);
pages[currentPage] =
<div className='nav__row2-item--selected'>{pages[currentPage]}</div>;
debugger;
return (
<nav>
<div className="nav__row1">
<div className="nav__logo">Worksheet Generator</div>
<div style={{cursor: 'pointer'}} onClick={this.props.logout}>
Logout
</div>
</div>
<div className="nav__row2" >
{ Object.values(pages).map( page => (
<div key={shortid.generate()}>{page}</div>)
) }
</div>
</nav>
);
}
}
If you want to dynamically add a class to your component, I recommend using classnames library. This is pretty much the way to do it when using external stylesheets.

Resources