I'm using gatsby js and trying to figure out how to have a page level side bar with Gatsby links that render a new component inside a div in the same page I can do this using react-router-dom but in Gatsby all I can find is how to create blog posts which is driving me nuts as every tutorial I find is the same blog post.
Here is my layout page /layouts/index.js
export default ({ children }) => (
<div id="layout">
<header>
<h3>Header</h3>
<MainNav />
</header>
{children()}
</div>
)
About Page
/pages/about.js
export default ({ location, match }) => {
console.log('location = ', location, 'match = ', match );
return (
<div id="about">
<SideBar />
<div id="content">
// . add child template or component for link clicked in sidebar
</div>
</div>
);
};
What I'm trying to do is when a user clicks on a link in the side bar stay on about but render a new component or template based on the gatsby-link clicked in the about sidebar.
The About SideBar component
/components/about/side-bar.js
const SideBar = () => {
return (
<div id="side-bar">
{/* <li><Link to='/about?sort=name'>work</Link></li> */}
<li><Link to="/about/work">work</Link></li>
<li><Link to='/about/hobbies'>hobbies</Link></li>
<li><Link to='/about/buildings'>buildings</Link></li>
</div>
)
}
Problem with the links above, they are trying to go to a new page called.
/about/work
This is not what I'm trying to do. Again I'm trying to make it stay on about but render a new component inside the content div.
Please help gatsby is so all over the place as far as docs goes. ok maybe its just me and not getting the docs clearly.
Thanks
UPDATE:
I tried adding a page suing createPage which works for me kind of but it doesn't pass the match.params id
gatsby-node.js
exports.createPages = ({ boundActionCreators }) => {
const { createPage } = boundActionCreators;
const myComponent = path.resolve('src/pages/about/index.js');
createPage({
path: '/about/:id',
component: myComponent
})
}
After a long time of trying to understand Gatsby and I can say I still don't as its docs are vast and not very clear. But once I started to look at the node-apis and onCreatePage it gave me some ideas. This is what the docs literally say.
onCreatePage
Called when a new page is created. This extension API is
useful for programmatically manipulating pages created by other
plugins e.g. if you want paths without trailing slashes.
So the only part in here that gives me a hint of this might be the key to helping me is this line. useful for programmatically manipulating pages created by other
plugins
Anyway this got me writing some code at least. Then about 3 hours later I found a plugin that was doing exactly what I was trying to do with this method. The plugin is called gatsby-plugin-create-client-paths key here is client-paths!!!!!
This makes life worth living! So in my case above I just wanted to be able to use Gatsby's router ( which is just react-router-dom behind the scenes), to pass me and id or value to routers match.params object. It still doesn't but what it does do is checks for any path after a prefix like /folder/ in my case '/about/work and recreate the page with a template component (in my case keep using pages/about/index.js), which is my template. Now that we have about/index.js rendering for ever link after /about/ then we can use some internal switch statement to handle the location that is been passed to /about/index.js. Still don't get match.params update but I do get props.location.pathname; which allows me to extract everything after the prefix to use in a switch statement to render my specific components based on the routes pathname. Enough rabbiting on here is a rough solution to show as an example.
So add the plugin as an npm install.
open up gatsby.config.js and add the below code to the exports.
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-create-client-paths`,
options: { prefixes: [`/about/*`] },
},
]
}
Then in my main about page pages/about/index
import React from "react";
import SideBar from '../../components/about/side-nav';
export default (props) => {
const { pathname } = props.location;
var n = pathname.lastIndexOf('/');
var pageId = pathname.substring(n + 1);
const page = () => {
switch(pageId){
case '':
return (
<div>Work Page</div>
);
case 'work':
return (
<div>Work Page</div>
);
case 'hobbies':
return (
<div>Hobbies Page</div>
);
case 'buildings':
return (
<div>buildings Page</div>
);
}
}
return (
<div id="about">
<SideBar />
<div id="content">
{page()}
</div>
</div>
);
};
Then in my sidebar I call it like this.
<li><Link to="/about/work">work</Link></li>
<li><Link to='/about/hobbies'>hobbies</Link></li>
<li><Link to='/about/buildings'>buildings</Link></li>
Hopefully this will help someone else out. After all this I'm starting to really question the bulk of gatsby especially with docs not been very clear. Based on the response to my question I guess not many people in stackoverflow's community are using Gatsby which is worrying when you need help. It does look like Gatsby's github community is very helpful but that should be for bug issues and not for questions like mine, but encouraging to see.
Hope this helps someone.
Related
I'm creating a portfolio website using React.
It is a single page website and each section of the website is its own separate component.
My navbar component has links at the top that when clicked I want the page to scroll to the corresponding section further down the page. A traditional About is not working. Do I need to use react-router-dom or am I just missing something?
const App = () => {
return (
<div className='app'>
<Navbar />
<Hero />
<About />
<Projects />
<Footer />
</div>
)
}
export default App
You can use the refs to scroll the page to a section . Refs in react have many applications , you can read details on react official page. Here is an example to use ref for scrolling
const Scroll = () => {
const scrollTo = useRef();
return (
<div>
<button onClick = {()=>{scrollTo.current.scrollIntoView()}></button>
.
.
// Your Other divs
.
.
<div ref={scrollTo}>Scrolled</div>
</div>
);
}
You can use the window.scroll() for scrolling page to a coordinate
window.scroll({top:600,left:50,behavior:'smooth'})
Don't know what I was doing wrong but as Drew suggested in his answer, the classic <a> tag does work as expected.
Go forth and use those tags fellow React devs!
Right below the page you see a 'back to log' button. If someone clicks on that it will return to the left page. So in order to do so I thought using react router will do the job. But not sure how to fix that.Is there someone who can help me to point me to the right direction. Keep in mind that it will not open a new tab!
Link to working snippet to understand the bigger picture of my app. CodeSandBox snippet here
return (
<div>
{info.map((show: any) => {
console.log(show.show)
return (
<div key={show.show.id}>
<ReactMarkdown source={show.show.name}/>
{show.show.image && <img src={show.show.image.medium}/>}
{show.show.genres.map((showGenre: string, index: number) => {
return (
<div key={index}>
<ReactMarkdown source={showGenre}/>
</div>
)
})}
<div>
<Router>
<ul>
<li>
<Link to="/">See deta</Link>
</li>
</ul>
</Router>
</div>
</div>
)
})}
</div>
)
Check out this working example (I placed notes in each file):
https://codesandbox.io/s/modest-panini-hkzlv?file=/src/App.js
Overview:
there are several ways to do this
my suggestion is to use the history npm and create a history.js file
you will also need to use Router from react-router-dom
in your parent component, or in a Context provider, you can store your state
in the parent component, use react-router-dom Switch and Route to place routes
depending on implementation, conditionally render show route for async state update
in your search component, place all your Links using react-router-dom
on click Links should also update app's state of your specific selection
this selection gets passed to the show route/component
in the show component attach an on click that uses history.goBack and resets state
this might help you:
this.props.history.goBack();
it goes back to prev page
Completely new to React, so please forgive my ignorance.
Using ReactStrap for this example. I'm attempting to define {url} dynamically, to where it's defined under src/components in its own jsx file:
const NavLink = ({ url },{ children }) => (
<RSNavLink href={url}>
{children}
</RSNavLink>
);
but when I call the component elsewhere, I can insert whatever hyperlink I'd like:
<NavLink href="/">Hello, World!</NavLink>
I'm not able to find documentation specific to what I'm looking for (or at least, I may not be searching for the right thing). If anyone could point me in the right direction, it'd be much appreciated.
In NavLink Component you are wrongly destructing props value and give exactly same name as props. change it like this
const NavLink = ({ url ,children }) => (
<RSNavLink href={url}>
{children}
</RSNavLink>
);
<NavLink url="#">
Hello World
</NavLink>
check demo in stackblitz
I just started on react router.
I have two questions. What is the difference between using <Link to="/page"> and <a href="page">? Both make the exact same get request to /page but I get an error when I use <a href="page"> but it works when I use <Link to="/page"> when I am nesting routes. I don't understand, how there could be any difference, when I know for fact that both render to exact same url?
Second is the weird arrow function in react router v4 documentation
const About = () => (
<div>
<h2>About</h2>
</div>
)
I know () => {} these are new in ES6 but I cannot find anything on normal brackets instead of parentheses. What are they?
Edit
My index.js class (I have all the imports)
render((
<Router>
<div>
<Route component={App}/>
</div>
</Router>
), document.getElementById('root')
);
My App.js class
class App extends Component {
render() {
return (
<div className="container">
<header>
<span className="icn-logo"><i className="material-icons">code</i></span>
<ul className="main-nav">
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/teachers">Teachers</Link></li>
<li><Link to="/courses">Courses</Link></li>
</ul>
</header>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/teachers" component={Teachers}/>
<Route path="/courses" component={Course}/>
</div>
);
}
}
export default App;
The error I'm getting.
Cannot GET /about on the browser when I try to move to localhost:8080/about. However, when I click the about button, it goes to exactly the same url /about and renders perfectly
This may be a bit late to address your issue and you may well have figured it out. But here's my take:
First:
What is the difference between using <Link to="/page"> and <a
href="page">
On the surface, you seem to be comparing apples and oranges here. The path in your anchor tag is a relative path while that one in the Link is absolute (rightly so, I don't think react-router supports relative paths yet). The problem this creates is say you are on /blah, while clicking on your Link will go to /page, clicking on the <a href='page' /> will take you to /blah/page. This may not be an issue though since you confirmed the correctness of the url, but thought to note.
A bit deeper difference, which is just an addon to #Dennis answer (and the docs he pointed to), is when you are already in a route that matches what the Link points to. Say we are currently on /page and the Link points to /page or even /page/:id, this won't trigger a full page refresh while an <a /> tag naturally will. See issue on Github.
A fix I used to solve my little need around this was to pass in a state property into link like so <Link to={{pathname: "/page", state: "desiredState"}}>Page</Link>. Then I can check for this in the target component's (say <Page />) componentWillReceiveProps like so:
componentWillReceiveProps(nextProps){
if (nextProps.location.state === 'desiredState') {
// do stuffs
}
}
Second question:
the weird arrow function in react router v4 documentation... I cannot find anything on normal brackets instead of parentheses. What are they?
Arrow functions; again #Dennis and #Jaromanda X have kind of addressed it. However, I've got three bits to add:
When you have () => blah without the curly braces {}, you are implicitly returning whatever follows the => in this case blah. But when you have curly braces immediately after the arrow, then it's now your responsibility to return something if you so desire. So () => blah (which by the way is synonymous to () => (blah)) will be more similar to () => { return blah } and not () => { blah }.
So what happens if you want to return an object: { blah: blah }; this is what #Jaromanda X was pointing at. You will then need to do () => ({ blah: blah }) or simply () => ({ blah }) for implicit return or you could return explicitly like so () => { return { blah: blah } }.
My third bit is to point you to MDN
Hope it helps.
The href attribute would trigger a page refresh which would reset the application states. However the link and navlink of react-router doesn't trigger a page refresh. Since React is used to create single page applications most of the time make sure you choose Link or Navlink when working with routing
The component allows you to do more than the normal link element. For instance, because it's a React component you have the benefits of having a state and what not (if you want that). You can see more documentation on here. Without the error I'm not sure what happens, but I suspect the routing library wants you to use the component, over a normal html element.
With regards to () => {} this is a construct which is called an anonymous function, or a lambda expression. It's basically the same as saving a function in a variable: var x = function(){ return (<div>...) }; if you have anything in the first parenthesis, it's a parameter which you have access to: const x = (y) => return y*2; The reason it's done in React is to expose the function scope to the component it lies in.
There is no better then looking at the code source.
https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/Link.js
You can see that Link is a component, that internally use history. Which is the module|library behind the history and navigation for react-router. And come with different modes (in memory history, browserHistory, hashHistory. And even custom).
Yea as a similarity it render an anchor tag but the default behavior is overridden (preventDefault()). They could have used just a div. But not completely right. As for the reason bellow.
So basically it work like that:
Observe the condition bellow
if (
!event.defaultPrevented && // onClick prevented default
event.button === 0 && // ignore everything but left clicks
(!this.props.target || this.props.target === "_self") && // let browser handle "target=_blank" etc.
!isModifiedEvent(event) // ignore clicks with modifier keys
) {
}
if the condition above is met. It will use history (push or replace). Otherwise it will leave the browser normal behavior. And so in that case it will be just a normal anchor tag <a />. Example letting the browser handle target='blank'. The condition are well explained.
Then depending on the type of history object. The behavior change. Not the behavior of ` itself. But just the result of the history object type.
In resume:
<Link /> is a component, that render a <a /> anchor tag. However in the main conditions the default behavior is prevented (preventDefault()). That allow it to apply the change to the history object (onClick event). Which react-router navigation is based on. And on the some conditions as mentioned above. It just fall back to the browser behavior. And just be exactly a <a /> anchor tag (no preventDefault()).
For the use. If you are using React-router. Then you just need to use Link.
I am making my first web-app using react, react router v4, and redux/react-router-redux.
it is a shopping site. I have my list of my products saved in the store and can access them fine. I have produced a products list page, when clicking on the products' image i have routing setup to take me to a new url(/'productmodel').
Currently I have a 'ProductPage' component for which I have passed in props relevant to the specific product, for each corresponding route, within my router. This seems like a very long way of doing things.
What I would like to do is render for each of the routes and then have the ProductPage component itself, render the right product depending on the route (URL address).
What is the best way to do this??
Thank you in advance :)
Are you after something like this?
ProductsListPage:
....
render() {
<div>
{this.props.products.map(product =>
<Link key={product.id} to={"/product/"+product.id}>
{product.name}
</Link>
)}
</div>
}
....
ProductPage:
....
componentWillMount() {
this.props.actions.getProduct(this.props.match.params.productid);
}
....
render() {
<div>
<span>{this.props.product.name}</Link>
</div>
}
....
Your route for product page would look like this:
<Route exact path="product/:productid" component={ProductPage} />
So what's happening here is when you click on a product on Products List page, you get redirected to the product page that has the product id as a parameter. On component mount on product page, you retrieve the product passing the productid from params (url).
ok so I manage to do it!
I created a ProductPageContainer inorder to pass my products list from the store:
function mapStateToProps(state) {
let products = state.kitsProducts;
return {
products: products
};
};
I then, within my component, got the productid from the params; as suggested by Hossein:
createProductPageComponent() {
let activeProduct = this.props.products.filter(product => product.id === this.props.match.params.productid)
return activeProduct.map(product =>{
return (
<ProductPage
key={product.id}
brand={product.brand}
productName={product.model.toUpperCase()}
price={"£"+product.price}
productImage={product.image}
text={product.text}
/>
)
})
}
and now my product page renders to the the right route, depending on which product was selected in the previous product list page. And the right product, depending on what route it has been rendered in.
Thanks Hossein!!! You helped me more than you may think!