How to provide dynamic content pages in react? - reactjs

After submitting a form I need to display a success page. It's content is determined by the form values and it consists of headline, static text and a form value. In the end 5 different success pages are possible. How can I best provide the data in a separate success page? Do I need 5 different success pages or is there dynamic solution possible?
Routes
<Route path="/form" element={<MyForm/>} />
<Route path="/success" element={<SuccessPage/>} />
success page templates
<h1> Headline A </h1>
<p> Text A <p>
<span> {form value name} </span>
<h1> Headline B </h1>
<p> Text B <p>
<span> {form value name} </span>

There are multiple solutions to this problem.
You can put the form values on the query string. Then use that to display content.
import { useLocation } from 'react-router-dom';
// http://localhost:4000/success?valueA=101
const SuccessGenericComponent = () => {
const search = useLocation().search;
const valueA = new URLSearchParams(search).get("valueA");
console.log(valueA); //101
return (
<h1> Headline A </h1>
<p> Text A <p>
<span> {valueA} </span>
)
}
You can use a state management library like Redux, React.Context, or even just your localstorage to handle the state of your form and access it in a different component. This will involve an initial setup though.

You can use generic component for this usecase, as your template data is same, No need for different pages.
You can add conditional rendering to differenciate the page or define a type of page you need from service and display based on that.

Related

Why infinite scroll pagination uses useState not using DOM append instead?

I have seen many tutorials and articles which use useState to store all the records on a state instead of using ReactDOM or something to add HTML elements.
For example, they keep previous data and merge with new data and after that set in a useState hook
return [...new Set([...prevBooks, ...res.data.docs.map(b => b.title)])]
I have 2 questions
Isn't it a performance problem to have all the records in the state and to print it in JSX by map? There may be millions of records.
If I want to add a React component to the end of a div#ID, what should we do in the pagination?
For example, I have this block code:
<article className="col-sm">
<div className="row client-home-header-post-article-row" id="BlogsPosts">
{posts.entries.map((item) => (
<BlogItem post={item} key={item.id} size={4} />
))}
</div>
</article>
And with an action function like const showMore, add another <BlogItem post={item} key={item.id} size={4} /> to the bottom of BlogsPosts ID (like e.append)
I saw this post Append component in React, he suggested without recommending to use ReactDOM.createPortal, but I tested it like this, and it does not work
ReactDOM.createPortal(
<BlogItem post={item} key={item.id} size={4} />,
document.querySelector("#BlogsPosts")!
)
The posts I saw:
Append component in React
Reactjs append an element instead of replacing
How do you append a React Component to an html element
How to append React components to HTML element using .append()
How can I append a React component to an html element i?
Using document.querySelector in React? Should I use refs instead? How?
Append Element to an Existing Element React
Thank you in advance
Isn't it a performance problem to have all the records in the state
and to print it in JSX by map? There may be millions of records.
It depends. A performant server should try to keep the metadata it returns to the browser for each book small. That is, each book object should try to to contain minimal information like a title, author, price, and thumbnail URL, not all the data that the database knows about the book. Something like this:
{
"title":"MyBook",
"author":"Jeff Bezos",
"price":19.99,
"price_unit":"USD",
"thumbnail":"https://websitename.s4-bucket.region.UUID-1234"
}
Reducing the amount of data will boost performance by making searching through this data faster on the browser. Each object of this size is also less than 500 bytes, so if you are obtaining 50 items at a time you're only retrieving 25KB data per request, which with modern speeds only takes a few milliseconds.
If I want to add a React component to the end of a div#ID, what should
we do in the pagination?
You can use a useMemo hook which will update the BlogItem components you have rendered when the posts change. Something like this should work:
const blogItems = useMemo(()=>posts.entries.map((item) => (
<BlogItem post={item} key={item.id} size={4} />
)), [posts.length])
Then in your JSX just do this:
<article className="col-sm">
<div className="row client-home-header-post-article-row" id="BlogsPosts">
{blogItems}
</div>
</article>

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>

System to manage reactjs applications

I get how to specify where to render the reactjs application by using the render method and specifying the html tag where it should be rendered.
What I do not understand is how you can have a list of react.js applications that is dynamically loaded into that same HTML tag.
For example there is a sidebar which is dynamically created to give a user a list of N number of react.js applications. When the user clicks on one of the links it loads that application into the HTML tag (div or whatever) container on the right.
I am sure this may be something easy but have been struggling with this concept for awhile.
Would appreciate any inputs anyone has on this.
If you truly had multiple full apps you wanted to swap out, you'd have to manually mount and unmount them. Something like a function like this, that unmounts the previous app, then mounts a new one. Example
function swapApp(App) {
const appNode = document.getElementById('app')
ReactDOM.unmountComponentAtNode(appNode)
ReactDOM.render(<App />, document.getElementById('app'))
}
But that would be a pain. So, typically, that menu and the content being changed are all part of the same react app. This app would render the menu, keep state about what item you clicked, and then render some components conditionally, depending on what was clicked.
Something like this example
function App() {
const [showingItem, setShowingItem] = React.useState(null)
return (
<>
<p><a href="#" onClick={() => setShowingItem('A')}>Show Item A</a></p>
<p><a href="#" onClick={() => setShowingItem('B')}>Show Item B</a></p>
{showingItem === 'A' ? <AppA /> : null}
{showingItem === 'B' ? <AppB /> : null}
</>
)
}

Pass props to props.history.push('path') in 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].

switching between certain component on a page using nested route in react router

I am trying to create a job site. Following pages shows list of all the jobs which is shown once user hits search button from home page. So basically this is the second page.
In this page i am catching all the search parameter from url and fetching data from api and result is shown as below:
Once the user clicks individual joblist, detail page should load on the same page without changing header and fixed component with unique URL for the detail page. Expected result shown below:
My Problem:
I manage to create a nested Route, which renders detail page on the same page and also has a unique url. But it renders on top of existing job list. I mean if user clicks on joblist1, detail page renders on top of subsiquent list(above list: 2, 3, 4). But expected result is to only render detail page but not list of jobs when individual job list is clicked.
My code: I have only shown part of the code for brevity and simplicity.
1) jobs.js: Passes state data to child component to show list.
return(
<div>
<div>
fixed component
</div>
<div>
<RouteHandler />
<JobLists joblists={this.state.joblists} />
</div>
</div>
)
2) jobList.js: uses .map function to go through all data and handleclick function generate url and opens that url once user clicks individual link. Router catches nested route and loads value inside jobs.js in " ".
handleClick: function(i){
var base_path = window.location.protocol + '//' + window.location.host;
base_path += '/#/jobs-detail';
window.location= base_path;
},
render: function(){
var jobListRow = this.props.joblists.map(function(jobrowobj, i){
return(
<div key={jobrowobj.id} onClick={this.handleClick.bind(this, i)}>
<img src={jobrowobj.logo} alt="" />
<h3>{jobrowobj.title}</h3>
</div>
)
}.bind(this));
return(
<ul id="joblists">
{jobListRow}
</ul>
)
}
3) Route file:
var routes = (
<Route handler={App}>
<DefaultRoute handler={Home} />
<Route name="jobs" path="jobs" handler={Jobs}>
<Route name="jobs-detail" handler={JobDetail} />
</Route>
<NotFoundRoute handler={NotFoundPage} />
</Route>
);
I am not sure what is the best way to switch certain section (component) on a page as in my case switching between joblist component and jobdetail component. As you can see i am only able to load other component on top of existing component which is not the expected result.
Also would appreciate if any hint is given to maintain scroll position on the job list on user hitting back button.
I suggest you to upgrade your react-router to 1.0.0-rc1, and the API is more clear. Your problem is similar to the official introduction. The nested component will be passed as this.props.children, and you can insert it into the jobListRow.
About the scroll position, there's a github issue discussing how to restore it :)

Resources