React router not rendering the component on click of a Array item - reactjs

I'm creating a component which has array of items and showing them on one page using map function.
Need help on how I can show a detail view of a item by updating route value dynamically based on the item I clicked.
Currently when I click on a item know more though the url change to some wrong value I don't see any change in the page.
Component:
{location[area].map((item, index) =>
<div key={index} className="col-md-4 mt-3">
<div className="card">
<div className="card-body">
<h5 className="card-title">{item.title}</h5>
<p className="card-text">{item.description}</p>
<Link to={'/view/${item.title}'} onClick={() => addDetail(item)} className="btn btn-primary">Know more</Link>
</div>
</div>
</div>
)}
Router:
<Route path='/view/:id' exact component={DetailPage} />
DetailPage Component:
class DetailPage extends Component {
render() {
const selectedItem = this.props.selectedItem;
return(
<div>
<DetailedView selectedItem={this.props.selectedItem} />
</div>
);
}
}
Result Anchor Tag:
<a class="btn btn-primary" href="/view/${item.title}">Know more</a>
Onclick result url:
http://localhost:3000/view/$%7Bitem.title%7D

You need to use backticks for the to prop of your Link components so that template literals will be used and the variable will be inserted into the string.
<Link
to={`/view/${item.title}`}
onClick={() => addDetail(item)}
className="btn btn-primary"
>
Know more
</Link>

Well,As #tholle suggested use template literal.Your route seems fine, just make use of react life cycle method componentWillReceiveProps in the Detail Page Component.Here is the code
class DetailPage extends Component {
componentWillReceiveProps(nextProps){
if(this.props.match.params.id !== nextProps.match.params.id){
//make a call with updated nextProps.match.id or filter the existing data store
with nextProps.match.id
}
}
render() {
const selectedItem = this.props.selectedItem;
return(
<div>
<DetailedView selectedItem={this.props.selectedItem} />
</div>
);
}
}

Related

How to solve react hydration error in Nextjs

I have created small nextjs page using wordpress REST API, Now react-hydration-error error show this page.I am using react html parser npm. How do I solve this error. could you please solve this error.
my code:
import Image from 'next/image'
import React ,{Component}from 'react'
import Link from 'next/link';
import { BiCalendar } from "react-icons/bi";
import ReactHtmlParser from 'react-html-parser';
export default class Blog extends Component{
constructor(props){
super(props);
this.state={
data: props.bloglist,
isLoading: true,
dataLoaded: false,
};
}
render(){
if (!this.state.data) {
return null;
}
console.log(this.state.data)
return(
<>
<div className="container blog-section">
<div className='row'>
<h2>Latest Posts</h2>
</div>
<div className='row'>
{
this.state.data.map(((x,i) =>(
<div className='col-md-4 boxs text-center' key={i}>
<div className='bg-info'>
<img src={x.images.large} className='img-fluid'/>
<h3>{x.title.rendered} </h3>
<p className='shopping'><span><BiCalendar/> {x.date}</span> </p>
{/* <p dangerouslySetInnerHTML={{__html: x.excerpt.rendered}}></p><span><BiShoppingBag/> {x.slug}</span> */}
<p class='expert'>{ReactHtmlParser(x.excerpt.rendered)}</p>
<Link href={"/blog"+"/"+x.slug+"/"+x.id } passHref={true}><p className='readmore'><span>Readmore </span></p></Link>
</div>
</div>
)))
}
</div>
</div>
</>
)
}
}
My original issues:
paragraph coming this format <p>If you have heard that there are ways to make money while shopping in the UAE and would lik</p> from API, So I converted to html.
I had this error, in my case I had <p> tag nested inside another <p> tag,
I was using Typography (MUI v5) to render text, switching to <Box> from <Typography> fixed the error.
We use components to build the React-based websites, These components are made using HTML tags. It is very important not to nest the same HTML elements.
For Example:
function Logo() {
return (
<Link href="/">
<a>
<Image
src="/images/logo.svg"
width={100}
height={75}
/>
</a>
</Link>
);
}
export default Logo;
Above is the Logo Component which has already the <a></a> tag inside it.
In this example, you will get the React Hydration Error if the <a> tag is used inside another <a> tag.
<a href="#">
<Logo />
</a>
So do not include the same HTML tags, which are hidden inside the
components to avoid react hydration error.
In my case I am using NextJS and I had a dropdown with react-select, the default value was changing after a small calculation, that does not like to nextjs, this is my previous code:
<Select options={seasons}
onChange={() => setSeason(e.value)}
defaultValue={seasons.find((x) => x.value == season) ? seasons.find((x) => x.value == season) : seasons[0]}
/>
So, I changed that calculation to the useEffect and initialized the react-select dropdown only when that value was calculated,now this is my current code that works:
{defaultSeason && (<Select options={seasons}
onChange={() => setSeason(e.value)}
defaultValue={defaultSeason}
/>)}
So, basically check that the defaultValue or anything else does not change after the html is sent to the client part in NextJS.
Follow these: https://nextjs.org/docs/messages/react-hydration-error
Or try deleting <a> within <Link> maybe.
My first code was this:
const isUserLoggedIn = is_user_logged_in()
// is_user_logged_in() checks for cookie of user token and returns boolean
Got the error about hydration
Then changed code to this:
const [isUserLoggedIn, setIsUserLoggedIn] = useState(null)
useEffect(() => {
setIsUserLoggedIn(is_user_logged_in())
}, [])
Renders was like this:
{isUserLoggedIn ? (
<>
{/* After login */}
<Profile USER={USER}/>
</>
) : (
<>
{/* Before login */}
<SigninButtons/>
</>
)}
And error solved
You can also check this
https://nextjs.org/docs/messages/react-hydration-error
Just try restarting the server. npm run dev. It worked for me. I was using react-hot-toaster.
Also try to check if you have sth like this:
<p>
<div>
Hello
</div>
</p>
div cant be inside p tag

Passing objects to navigation / .map objects single page component

I am learning React and I have a problem that I don't know how to solve, so If you can please help me with it.
I want to create a simple react app, where I create 3 components with .map() method. I would like to pass the data to Head component that I can access to every single card separately.
I have used .map() method but all cards are on one page. Can I do that with .map() method, and pass data to nav component?
function Card(props) {
return (
<Router>
<div id="wrapper">
<Link to="#"><button id="request" href="#">
REQUEST
</button></Link>
<h1 id="naslov">{props.name}</h1>
<p>{props.key}</p>
<div id="povez">
<a id="srcek"><FiHeart /></a>
<button id="follow">Follow</button>
<a id="kocka"><FiExternalLink /></a>
</div>
<div>
<h4 className="he4der">ORIGIN</h4>
<button id="origin">COUNTRY</button>
</div>
<div>
<h4 className="he4der">GENRE</h4>
<button id="origin">ROCK</button>
</div>
<div>
<h4 className="he4der">SUBGENRES</h4>
<button id="origin">ROCK</button>
<button id="origin2">METAL</button>
</div>
<div>
<button id="origin">ROCK</button>
<a id="kocka2"><FiExternalLink /></a>
</div>
<div id="breaker"></div>
<div id="socialinks">
<div id="social">
<span id="sicon"><FaFacebookF /></span>
<span id="sicon"><FaTwitter /></span>
<span id="sicon"><FaInstagram /></span>
<span id="sicon"><FaSpotify /></span>
</div>
<div id="plus">
<a id="kocka2"><FiExternalLink /></a>
</div>
</div>
</div>
)
}
export default Card
I would like to pass artist_name to nav component, so that when I
click on that name I can access card component that is related to that
name/artist.
Instead of passing a single artist it may make more sense to use all_artists and map your links. If all_artists isn't available globally it can be passed as a prop.
const Head = (props) => {
return (
<div id="header">
<img src={Logo} id="logo" />
<ul>
{props.artists.map(({ artist_id, artist_name }) => (
<li key={artist_id}>
<Link to={`/artist/${artist_name}`}>{artist_name}</Link>
</li>
)}
</ul>
<div id="icns">
<i><FiSearch /></i>
<i><FiAlignJustify /></i>
</div>
</div>
)
}
Pass all_artists to Head and render a dynamic route to extract name from the path to be passed on to Card on the name prop. Notice the Router was moved here to provide a routing context for both the Link components in Head and the Route component in App.
function App() {
return (
<div className="App">
<Router>
<Head artists={all_artists} />
<Route
path="/artist/:name"
render={({ match: { params: { name } } }) => (
<Card name={name}>
)}
/>
</Router>
</div>
);
}
Card will need to utilize a lifecycle to "react" to any changes on the name prop, like fetching/loading specific artist data from an API endpoint or redux state.
You need to pass to your component(that is rendering the card) what is the artist_id( as props) that you want to display and then you could use find() array method to search/filter the object that you need inside the array since you already have the ID of the artist.
and you might want to use switch and map() method on your router if you want all artist to be in your navigation
something like this:
<Switch>
{ all_artists.map( artist =>
<Route path=`/{artist.id}` render={ <CardComponent clickedArtist={artist} /> }/>
)}
</Switch>

Switch to different component after button click with React

I am absolutely new to React and need to create a button to take the user from a current component (MovieView) to the main one (MainView). I managed to create it properly and I used onClick to trigger the display of MainView. I know I cannot call a class from a function (so as console says), so I created a second function to be called and trigger MainView. But as you may wonder, it does not work. This is the code I am using:
import React from 'react';
import { MainView } from '../main-view/main-view';
function NewButton() {
return <MainView />;
}
export class MovieView extends React.Component {
constructor() {
super();
this.state = {};
}
render() {
const { movie } = this.props;
if (!movie) return null;
return (
<div className="movie-view">
<img className="movie-poster" src={movie.imagePath} />
<div className="movie-title">
<span className="label">Title: </span>
<span className="value">{movie.title}</span>
</div>
<div className="movie-description">
<span className="label">Description: </span>
<span className="value">{movie.plot}</span>
</div>
<div className="movie-genre">
<span className="label">Genre: </span>
<span className="value">{movie.genre.name}</span>
</div>
<div className="movie-director">
<span className="label">Director: </span>
<span className="value">{movie.director.name}</span>
</div>
<div className="back-movies">
<button onClick={NewButton}>Back</button>
</div>
</div>
);
}
}
Could you any seeing this to point the path to take or how I can call this MainView class properly. I understand it's a simple task, but I am afraid I still haven't absorbed React principles yet. Thanks in advance to you all.
Without introducing additional dependencies, probably the easiest way for this example is to use state to track if the button has been clicked. If it has been clicked, render MovieView, if not render MainView.
For this you need to following:
Set state variable that tracks that MainView should be rendered in onClick event. (boolean will probably suffice)
in render():
if the state var is false, render the content of MovieView.
if the state var is true, render the MainView component.
Implementation details are left as an exercise :)
If you're using a router already (like react-router) you could probably just update the url to an url that is linked to the MainView component..
I make the Class into a Function Component instead, and implement it like this :-)
import React from "react";
import { MainView } from '../main-view/main-view';
export const MovieView = ({ movie }) => {
const { showMainView, setShowMainView } = React.useState(false);
if (!!movie) {
return null;
}
function toggleMainView() {
setShowMainView(true);
}
return (
<React.Fragment>
{ showMainView ? (
<MainView />
) : (
<div className="movie-view">
<img className="movie-poster" src={movie.imagePath} />
<div className="movie-title">
<span className="label">Title: </span>
<span className="value">{movie.title}</span>
</div>
<div className="movie-description">
<span className="label">Description: </span>
<span className="value">{movie.plot}</span>
</div>
<div className="movie-genre">
<span className="label">Genre: </span>
<span className="value">{movie.genre.name}</span>
</div>
<div className="movie-director">
<span className="label">Director: </span>
<span className="value">{movie.director.name}</span>
</div>
<div className="back-movies">
<button onClick={() => toggleMainView()}>Back</button>
</div>
</div>
)}
</React.Fragment>
);
};
export default MovieView;
If you need to add or remove components or show a new view based on an user action in react we need to do this via state .
import React from 'react';
import { MainView } from '../main-view/main-view';
export class MovieView extends React.Component {
constructor() {
super();
this.state = {
showMainView: false;
};
}
toggleMainView = () => this.setState(prevState => ({ showMainView:
!prevState.showMainView}))
render() {
const { movie } = this.props;
const { showMainView } = this.state;
if (!movie) return null;
return (
<div className="movie-view">
<img className="movie-poster" src={movie.imagePath} />
<div className="movie-title">
<span className="label">Title: </span>
<span className="value">{movie.title}</span>
</div>
<div className="movie-description">
<span className="label">Description: </span>
<span className="value">{movie.plot}</span>
</div>
<div className="movie-genre">
<span className="label">Genre: </span>
<span className="value">{movie.genre.name}</span>
</div>
<div className="movie-director">
<span className="label">Director: </span>
<span className="value">{movie.director.name}</span>
</div>
<div className="back-movies">
<button onClick={this.toggleMainView}>Back</button>
</div>
// render the mainView based on the state
{ showMainView && <MainView />}
</div>
);
}
}
I reached a solution that I'd not do with the help I had here. I have first pasted here some pieces of code but, I believe it can be helpful to give the whole answer.
So, the question I placed here was regarding a component called <MovieView, that was a part of an app that interacted with another component (in another file) called MainView.
The solution was the insertion of the method onClick on the live 14 of <MovieView> and the button on line 39 that you can see at this file on Github. If the file was updated, check the version for November 22nd.
The solution had, however, been found in the main component, <MainView>, adding code on lines 48 and 49. Here is the link to the file: https://github.com/cgobbet/react-client/blob/master/src/components/main-view/main-view.jsx
The repository with the whole app is on this page: https://github.com/cgobbet/react-client.
And sorry for the reviewers (I had accidentally deleted part of the fantastic answer submitted by #nick-miller.

How to change the state in conditional - React

I need to change the state of sibling components in my React App.
I use state and setstate
I need to change the state of sibling components. When loading the page, there must exist (visible in the page) <BkUser /> and when clicking "button id =" ds-visual "it must be deleted (<BkUser /> mustn't exist) and there must exist <BkDescanso />.
When you click on <BkSleep /> (in the div parent) you should remove <BkDescanso /> and show <BkUser />
This is the web.
There should never be <BkUser/> and <BkSleep> at the same time. <Bkuser /> is the blue block and <BkDescanso /> is the red block
This is my code:
Edit: I edit my original code because I fix the problem. This is the final OK Code. In the end the most important thing was the state conditional
{
this.state.usuario ? (<BkUser handleClick = {this.handleClick} usuario={this.state.usuario}/>): (<BkDescanso handleClick = {this.handleClick} usuario={this.state.usuario}/>)}
import React, { Component } from 'react';
class Header extends Component {
constructor(props) {
super(props);
this.state = {
usuario: true,
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
usuario: !state.usuario
}));
//alert("Works button");
}
render(){
return (
<header className="header">
<div className="configuracion">
{
this.state.usuario
? (
<BkUser handleClick = {this.handleClick} usuario={this.state.usuario}/>
)
: (
<BkDescanso handleClick = {this.handleClick} usuario={this.state.usuario}/>
)}
<div className="content-btn">
<button id="config" className='btn btn--rounded'><span className="ico-configuracion"></span></button>
<button id="salir" className='btn btn--rounded'><span className="ico-exit"></span></button>
</div>
</div>
</header>
);
}
}
class BkUser extends Component{
render(){
return ((
<div className='usuario'>
<img src="../img//usuario.svg" alt="Imagen usuario"/>
<div className="content-usuario">
<span id="nm-usuario" className="h4">Hermione Jane Granger</span>
<span id="tp-usuario" className="h5">Supervisor</span>
</div>
<div className="content-descansos">
<div className="botones">
<button id="ds-visual" className='btn btn--rounded' onClick={this.props.handleClick}><span className="ico-visual"></span></button>
<button id="ds-admin" className='btn btn--rounded'><span className="ico-tiempo-administrativo"></span></button>
<button id="ds-otros" className='btn btn--rounded'><span className="ico-descanso"></span></button>
</div>
<div className="ds-actual">
<span id="ds-tipo">Administrativo</span>
<span id="ds-tiempo">00:08:47</span>
</div>
</div>
</div>
));
}
}
class BkDescanso extends Component {
render(){
return ((
<div className='usuario descanso' onClick={this.props.handleClick}>
<h3>Finalizar descanso</h3>
</div>
));
}
}
export default Header;
Right now handleClick works but always exist BkUser and BkDescanso. I need only one to exist. If you click on id = "ds-visual" the bkUser block should disappear and BkDescanso appear. Then if you click on div className = 'user rest' in BkUser there should only be BkDescanso.
I think that it is not able to know when it is true and when it is false to show or hide
Thanks a lot for the help.
You're missing two things:
First you have to pass the handleClick function to the BkUser component, and then you have to call it via this.props.handleClick.
...
<BkUser handleClick={this.handleClick} usuario={this.state.usuario} />
....
<button
id="ds-visual"
className="btn btn--rounded"
onClick={this.props.handleClick}
>
ds-visual
<span className="ico-visual" />
</button>
CodeSandbox here.
Read more here.
You can change the state of the siblings by passing a function from the parent via props into them.
In the end your siblings are the children of their parent.
You can read this articles on how to change the state of child components.
React js change child component's state from parent component
https://medium.freecodecamp.org/react-changing-state-of-child-component-from-parent-8ab547436271
An other thing you could look into would be React Redux.

How to attach an event handler to a dynamically created item in React

I am new to React and am trying to add an onClick event handle to a list item which is created using map.
Currently, the page renders with the individual items each having their own button. However, when i click on any of the button an error
TypeError: Cannot read property 'AddThisToReadingList' of undefined.
Any help would be appreciated in understanding what is happening. Thanks in advance.
import React, {Component} from 'react';
class ResultsList extends Component{
constructor(){
super();
this.state={
currentUserEmail: 'brad.ga.co',
currentBookId: {}
}
}
AddThisToReadingList(index){
console.log('this');
//set state
//axios request to get book info
//send to node update user
}
render(){
console.log('Rendering');
return(
<div>
<ul>
<ListedBooks data ={this.props.listFromGoogle}/>
</ul>
</div>
)
}
};
const ListedBooks= props =>{
if (props.data === undefined){
return (<div>Loading....</div>)
}
return(
props.data.map( (book, index)=>{
return <li key={index}>
<h4>Title: {book.volumeInfo.title}</h4>
<div>Author: {book.volumeInfo.authors.map((element, index)=> <p key={index}>{element}</p>)}</div>
<p>Description: {book.volumeInfo.description}</p>
<p>Release Date: {book.volumeInfo.publishedDate}</p>
<img src={book.volumeInfo.imageLinks.smallThumbnail}/>
<br/>
<button onClick={()=>this.AddThisToReadingList(index)}>Add to my reading list</button>
</li>
})
)
}
export default ResultsList;
This should do the trick:
In ResultsList:
<ListedBooks
data={this.props.listFromGoogle}
AddThisToReadingList={this.AddThisToReadingList.bind(this)}
/>
In ListedBooks:
<button onClick={()=>props.AddThisToReadingList(index)}>
Add to my reading list
</button>
ListedBooks knows nothing about this in ResultsList. this is, in fact, undefined in ListedBooks. You need to pass the method to ListedBooks as a prop in order to access it there.
Add additional data to button like
<button id="additem" data-index={index}></button>
Then you can add listener with native javascript
render() {
const button = document.getElementById('additem');
button.addEventListener('click', () => {
this.AddThisToReadingList(button.dataset.index);
})
return (
props.data.map( (book, index)=>{
return <li key={index}>
<h4>Title: {book.volumeInfo.title}</h4>
<div>Author: {book.volumeInfo.authors.map((element, index)=> <p key={index}>{element}</p>)}</div>
<p>Description: {book.volumeInfo.description}</p>
<p>Release Date: {book.volumeInfo.publishedDate}</p>
<img src={book.volumeInfo.imageLinks.smallThumbnail}/>
<br/>
<button data-index={index}>Add to my reading list</button>
</li>
})
)
}

Resources