I'm working on a personal website and I have a problem rendering a component dynamically using React Router. To me, everything seems correct but for some reason, it's not working.
I tried to follow the documentation and watched a couple of tutorials but I have been stuck for a long time so I feel like I need help on this one.
In this component, I want to render the 'Articles' component dynamically using the id
class JobCard extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
};
}
componentWillMount() {
fetch(url)
.then(data => data.json())
.then(result =>
this.setState({
data: result
})
);
}
render() {
const { match } = this.props;
const { data, value } = this.state;
return (
<>
<div>
{results.map((job, id) => (
<div key={id}>
<div key={job._id} className="blog-card">
<div className="meta">
<div className="photo">
<img src={job.img} alt="logo" />
</div>
</div>
<div className="description">
<h5>{job.position_name}</h5>
<p className="read-more">
<p>{job.location}</p>
<p>
<span className="learn-pow">
{" "}
<Link
to={{
pathname: `${match.url}/${job._id}`,
state: job
}}
>
{job.workplace_name}
</Link>{" "}
Enter Location
</span>
</p>
</p>
</div>
</div>
</div>
))}
</div>
}
<Route path={`${match.path}/:id`} component={Articles} />
</>
);
}
}
export default JobCard;
And here is the component that i want to render:
import React from 'react';
const Articles = ({ location }) => (
<div>
<h1>{location.state.name}</h1>
<h2>{location.state.email}</h2>
<h2>{location.state.place}</h2>
</div>
)
export default Articles;
When I click on the Card the URL is right so I get the id but I don't have access to the Article component. I tried to console log but nothing appears.
Instead of this
<Route path={`${match.path}/:id`} component={Articles} />
try doing this
<Route path={`${match.url}/:id`} component={Articles} />
Related
I am trying to redirect user to the URL hostUrl+'logout' when they click Yes button from the Are you sure you want to log out? popup dialog. Since my other parts of code is making use of
react router, I am wondering if I could make use of the same inside the following function :
logoutDialogClose = event =>{
if (event) {
this.setState({ displayLogoutPopup: false})
}
}
Someone in this post suggested making use of Link but not sure if that applies to my case or not.
Maybe I will need a separate function logoutDialogYes once above testing is done to separate Cancel and Yes clicks from the popup dialog.
My complete code is below:
const hostUrl = properties.baseUrlUI
class Main extends Component {
constructor() {
super();
this.state = {
isAuthenticated: false,
displayLogoutPopup: false,
};
this.logoutDialogOpen = this.logoutDialogOpen.bind(this)
this.logoutDialogClose = this.logoutDialogClose.bind(this)
}
componentDidMount() {
//some stuff here
}
loadDropDownObjects() {
// some axios call to store info in session storage
}
logoutDialogOpen = event =>{
if (event) {
this.setState({ displayLogoutPopup: true})
}
}
logoutDialogClose = event =>{
if (event) {
this.setState({ displayLogoutPopup: false})
/* <Link to="/">
<i className="fa fa-sign-out pull-right"></i>
Log Out
</Link> */
/* console.log("Testing host URL");
console.log(hostUrl);
axios.delete(hostUrl+'logout')
//axios.get(hostUrl+'logout')
.then(response => {
console.log("Reaching inside the response of logoutDialogClose function and printing the response below:");
console.log(response);
//keeping the setState outside until we test it on dev server
//this.setState({ displayLogoutPopup: false})
})
.catch (error => {
console.log("logout error", error);
}) */
}
}
render() {
const logoutDialog = this.state.displayLogoutPopup ? (
<div className="popup popup--icon -question js_question-popup"
id={this.state.displayLogoutPopup ? 'popup--visible' : ''}>
<div className="popup__background"></div>
<div className="popup__content">
<h3 className="popup__content__title">
Are you sure you want to log out?
</h3>
<p>
<button className="button button_accept" onClick={this.logoutDialogClose}>Yes</button>
<button className="button button--warning" onClick={this.logoutDialogClose}>Cancel</button>
</p>
</div>
</div>
) : null
return (
<div>
{
this.state.isAuthenticated ? (
<BrowserRouter basename={process.env.REACT_APP_ROUTER_BASE || ''}>
<div>
<div className="header">
<div className="header-logo">
<img src="images/logo.jpg"/>
<span>DATA</span>
</div>
<div className="logout_sec">
<div className="logout_icon">
<a href="javascript:void(0)" onClick={this.logoutDialogOpen}> <FontAwesomeIcon
style={{fontSize: '1.5em'}} icon={faSignOutAlt}/></a>
</div>
</div>
<ul className="header-list">
<li>
<NavLink exact to="/">
Home
</NavLink>
</li>
<li>
<NavLink to="/myprojects">My Projects</NavLink>
</li>
<li>
<NavLink to="/myassets">My Assets</NavLink>
</li>
<li>
<NavLink to="/datadownload">DataSet Download</NavLink>
</li>
<li>
<NavLink to="/assetbrowser">Asset Browser</NavLink>
</li>
</ul>
{/* Popup COde start */}
{logoutDialog}
{/* Popup code End */}
</div>
<div id="forNotification"></div>
<div>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/myprojects"
render={(props) =>
<div>
<Projects {...props}/>
</div>
}
/>
<Route exact path="/project" component={ProjectView}/>
<Route exact path="/datadownload" component={DataDownload}/>
<Route exact path="/assetbrowser" component={AssetBrowser}/>
<Route exact path="/datarequest" component={DataRequest}/>
<Route path='/404' component={NotFoundPage}/>
<Redirect to="/404"/>
</Switch>
</div>
</div>
</BrowserRouter>
) : null
}
</div>
);
}
}
export default Main;
Try adding this import:
import {withRouter} from 'react-router-dom';
Change the export statement to:
export default withRouter(Main);
Then, you should be able to access the history object inside Main through this.props.history. For example:
logoutDialogClose = event =>{
if (event) {
this.setState({ displayLogoutPopup: false})
this.props.history.replace(`/some/path`);
// OR
this.props.history.push(`/some/path`);
}
}
Edit:
Try replacing the export statement with this:
const MainWithRouter = withRouter(Main);
const MainWrapper = props => (
<BrowserRouter basename={process.env.REACT_APP_ROUTER_BASE || ''}>
<MainWithRouter {...props} />
</BrowserRouter>
);
export default MainWrapper;
I built a site with nextjs (with server express & API) and Reactjs.
I would like to create dynamic paginations because there is far too much result for statically generated, So I added server endpoint /publiations/page/:id, I put a getInitialsProps for keep the id in query
But actually, when I click on my main page /publications where my store is not empty to go to the next page (publications/page/1), the page reloads and the store is empty. How I can keep my store when I change route?
And here my publications/page/[id].js
const PublicationsPage = ({id}) => {
return (
<>
<MainMenu/>
<Search/>
<div className="flex">
<Sidebar fallback={<Loader/>}/>
<Cards type={'publications'} idPage={id} />
</div>
</>
)
}
PublicationsPage.getInitialProps = async function({ query: { id } }) {
return {
id: id
};
};
export default withAuthSync(PublicationsPage);
The cards components where i use the data of store :
components/Cards.js
const Cards = ({ idPage, cards, type }) => {
console.log(cards)
return (
<div className="cards">
<div className="content-filter-search">
<div className="content-newsearchresult">
<div className="global-name">Result: {cards.cards.length} articles found</div>
<div className="content-button-blue">
<Link href="/explorer">
<a>
<div className="button-blue">New search</div>
</a>
</Link>
</div>
</div>
<div className="content-filter">
{filters[idPage].map((item) => {
return <InputFilter key={item.id} data={item} callback={keepItems} />;
})}
</div>
</div>
<div className="wrapper-card">
<div className="cards-container">
{
!cards.loading ? cards.cards.slice(idPage * 9, idPage * 9 + 9).map((card) => (
<Card key={card.PMCID} data={card} />
)) : <Spinner color="black" size="100px" thickness={3} gap={5} speed="fast" />
}
</div>
</div>
<div className="text">
<Link href={'/publications/page/1'}><a>Next page</a></Link>
</div>
{
!cards.loading ? (
<div className="center">
<Pagination type={type} page={parseInt(idPage)} totalElement={cards.cards.length} numberPerPage={9} />
</div>
) : null
}
</div>
);
};
const mapStateToProps = state => ({
cards: state.cards,
})
export default connect(mapStateToProps)(Cards);
I use the same code for the route /publications and /publications/page/:id just I add the getInitialProps to keep the id of page. And I use connect redux in my Cards component
I have no error in the console just my store is reset because the page reload. I don't understand how I can make pagination with my store where is my data if when I change page the store is empty
Thanks
I want to pass data from Component A to Component B from a Route Link and then make an API request in Component B and I was able to pass it from Component A to B but couldn't figure out how to pass that data from inside render to a method that will make an API Request. hopefully, I was clear please look at the code below. and thanx in advance.
Component A
<ul>
{this.state.movies.map(movie => (
<li key={movie.imdbID}>
<img alt="img" src={movie.Poster} />
<h1>{movie.Title}</h1>
<p>{movie.Year}</p>
<button>
<Link to={{ pathname: "./productdetail", movieid: movie.imdbID }}>View More</Link></button>
</li>))}
</ul>
Component B
class ProductDetailPage extends React.Component {
state = {
movieIdSearch: []
};
movieIdRequest(id) {
axios.get(`http://www.omdbapi.com/?apikey=bcfe7e46&i=${id}`).then(res => {
const movieById = res.data;
this.setState({ movieIdSearch: movieById });
});
}
render() {
const {
Poster,
Title,
Year,
Released,
Runtime,
Genre,
Country,
Language,
Actors,
Plot
} = this.state.movieIdSearch;
return (
<div>
{/*how to pass this.props.location.movieid to a movieIdRequest method*/}
<img alt="img" src={Poster} />
<h3>{Title}</h3>
<div>
<p>{Year}</p>
<p>{Released}</p>
<p>{Runtime}</p>
<p>{Genre}</p>
<p>{Country}</p>
<p>{Language}</p>
</div>
<div>
<h5>{Actors}</h5>
<p>{Plot}</p>
</div>
</div>
);
}
}
I got a component here which should display gifs from giphy on a route base search term:
public render(){
return(
<div>
<Router>
<div>
<Route path='/:term' component={this.dispGiphy} />
</div>
</Router>
</div>
)
}
private readonly dispGiphy = (props) => {
this.getGifs(props.match.params.term)
return(
this.state.items.map(data => (
<img className="images" src={data.images.downsized.url} />
)))
}
public getGifs(inputValue){
// Get Request to the API and write the result to this.state.items
fetch("https://api.giphy.com/v1/gifs/search?limit=3&api_key=KEY_GOES_HERE&q=" + inputValue)
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result.data
});
},
(error) => {
this.setState({
error,
isLoaded: true
});
}
)
}
I also got a list, listing previous search terms:
public render(){
return(
<div>
<ul id="ul_LeftList">
{this.state.terms.map((term) => (
<li key={term}>
<Link to={`${term}`}>{term} </Link><button className="fa fa-cancel btnDelete" onClick={this.deleteElement(term)}
>X</button>
</li>
))}
</ul>
</div>
)
}
These two components are bound together in the app.tsx
return(
<div>
<div><header className="App-header">
<input type="text" placeholder="Search..." onChange={this.searchChanged} />
<button type="submit" className="fa fa-search searchButton" onClick={this.process} />
</header>
</div>
<Router>
<p className="leftList">
<LeftList terms={this.state.searchterms} />
</p>
</Router>
<p className="App-content">
<Router>
<Route path='/:term' component={Giphy} />
</Router>
<Giphy />
</p>
</div>
)
Now, when I click on a Link in the LeftList, I expect two things:
1. The Route will be updated with the search term I just clicked (working)
2. The Giphy component shows up the new gifs relating to the search term from the route (not working)
Actually I don't know what the problem is.
I have a BooksAPI file that contains the following search method:
export const search = (query) =>
fetch(`${api}/search`, {
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json'
},
body: JSON.stringify({ query })
}).then(res => res.json())
.then(data => data.books)
I'm trying to make it so that results start showing up when I type into the search bar, and I also want no books to show when the search bar is empty.
I have it working so that I can display results with the initial search, but if I try to backspace to type in a new term, I get a cannot read property map of undefined error, which makes sense, but I'm not sure how to address this. I can only do a new search if I refresh the page.
Any help is greatly appreciated.
This is what I have so far for App.js:
class BooksApp extends React.Component {
state = {
books: []
}
componentDidMount() {
BooksAPI.getAll()
.then((books) => {
this.setState(() => ({
books
}))
})
}
render() {
return (
<div className="app">
<div className="list-books">
<div className="list-books-title">
<h1>MyReads</h1>
</div>
<Route exact path="/" render={() => (
<BookList
books={this.state.books}
/>
)} />
<Route path="/search" component={Search} />
<Route path="/search" render={({ history }) => (
<Search
onSearch={(query) => {
this.search(query)
history.push('/')
}}
/>
)} />
<div className="open-search">
<Link to="/search">Add a book</Link>
</div>
</div>
</div>
)
}
}
export default BooksApp
And here is my Search component:
class Search extends Component {
state = {
query: '',
books: []
}
search = (query) => {
BooksAPI.search()
.then((books) => {
this.setState(() => ({
query,
books
}))
})
}
render() {
const { query, books } = this.state
return (
<div className="search-books">
<div className="search-books-bar">
<Link
className="close-search"
to='/'>Close
</Link>
<div className="search-books-input-wrapper">
<input
type="text"
onChange={(event) => this.search(event.target.value)}
placeholder="Search by title or author"
value={query}
/>
</div>
</div>
<div className="search-books-results">
<ol className="books-grid">
{books.map((book) => (
<li>
<div>
<p>{book.title}</p>
<p>{book.author}</p>
</div>
</li>
))}
</ol>
</div>
</div>
)
}
}
export default Search;
I had similar functionality I wanted to implement recently, I would recommend going to a library such as react-select to solve this problem rather than reinventing the wheel.