I'm new to Navigo JS Router and also it's my first time creating an application with a client side router. I have defined the routes that I am using, and all does not trigger page reload except whenever I navigate to the home page.
Search.js
const Search = {
render: () => {
return `
<section class="section">
<div class="row topbar">
<div class="col-xs-12 col-sm-3">
<h2 class="title text-xs-center">
Home Page
</h2>
</div>
</div>
</section>
`;
}
}
Detail.js
const Detail = {
render: () => {
return `
<section class="section">
<div class="row topbar">
<div class="col-xs-12 col-sm-3">
<h2 class="title text-xs-center">
Home Page
</h2>
</div>
</div>
</section>
`;
}
}
Searchbar.js
const Searchbar = {
render: async () => {
return `
<div class="site-search-container pull-right" id="site-search-container">
<div class="link-search-all">
View all
</div>
</div>`;
}
}
index.js
const render = async (view) => {
const header = null || document.getElementsByTagName('header')[0];
const content = null || document.getElementById('content');
const footer = null || document.getElementsByTagName('footer')[0];
// Render the Header and footer of the page
header.innerHTML = await Header.render();
await Header.after_render();
footer.innerHTML = await Footer.render();
await Footer.after_render();
content.innerHTML = await view.render();
await view.after_render();
};
const router = new Navigo(null, true, '#');
router.on({
'/*/reviews' : () => { render(Review) },
'/search' : () => { render(Search) },
'/*' : () => { render(Detail) }
});
// set the default route
router.on(() => { render(Home); });
// set the 404 route
router.notFound(() => { render(Error404); });
router.resolve();
As an example, when I navigate to the reviews page or the search page as stated in the 'Other Routes' above, the pages do not trigger a hard reload which is what I would expect. However, once I navigate to the home / root page, it always trigger a hard refresh which I do not want.
I have a link that I use to navigate to the home / root page:
Home Page
Can someone please help?
Thanks!
Related
So I've been struggling for a while with retrieving data from APIs and or retrieving local json files. I am using React axios but even with the normal fetch method I am having the same issues. When I fetch the endpoint and save the code, my jsx refreshes and the data appears on the screen but then when I refresh the page, it's no longer there and doesn't appear when I refresh again and again. I have no idea what I am doing wrong. I tried to retrieve the data on the parent and set it as props but still the same problem.
My child component:
import React, { useEffect, useState } from 'react';
import './Card.scss';
import axios from 'axios';
import { ellipsisIcon } from '../../constants/images';
import dataJson from './data.json';
const Card = ({ name, color, icon, currentTime }) => {
const [data, setData] = useState([]);
const [daily, setDaily] = useState([]);
const [weekly, setWeekly] = useState([]);
const [monthly, setMonthly] = useState([]);
useEffect(() => {
const fetchData = async () => {
const result = await axios.get('data.json');
setData(result.data);
setData(
data.filter((item) => {
return item.title === name;
}),
);
setDaily(data[0].timeframes.daily);
setWeekly(data[0].timeframes.weekly);
setMonthly(data[0].timeframes.monthly);
};
fetchData();
}, []);
return (
<div className="card" style={{ backgroundColor: `${color}` }}>
<img src={icon} alt={`${name} icon`} />
<div className="card__container bg-blue">
<div className="card__top-container flex">
<p className="text-white ">{name}</p>
<div className="card__top__elipse-container">
<img src={ellipsisIcon} alt="ellipsis" />
</div>
</div>
<div className="card__bottom-container">
{currentTime === 0 && (
<>
<h1 className="fs-900 text-white">{daily.current}hrs</h1>
<div className="card__bottom__prev-container">
<p className="text-accent ">
Yesterday -<span>{daily.previous}hrs</span>
</p>
</div>
</>
)}
{currentTime === 1 && (
<>
<h1 className="fs-900 text-white">{weekly.current}hrs</h1>
<div className="card__bottom__prev-container">
<p className="text-accent ">
Last Week -<span>{weekly.previous}hrs</span>
</p>
</div>
</>
)}
{currentTime === 2 && (
<>
<h1 className="fs-900 text-white">{monthly.current}hrs</h1>
<div className="card__bottom__prev-container">
<p className="text-accent">
Last Month -<span>{monthly.previous}hrs</span>
</p>
</div>
</>
)}
</div>
</div>
</div>
);
};
export default Card;
My App (Parent):
import { useState, useEffect } from 'react';
import Card from './components/Card/Card';
import {
pbImage,
ellipsisIcon,
exerciseIcon,
playIcon,
careIcon,
socialIcon,
studyIcon,
workIcon,
} from './constants/images';
const cards = [
{
name: 'Exercise',
color: 'hsl(var(--clr-exercise))',
icon: exerciseIcon,
},
{
name: 'Play',
color: 'hsl(var(--clr-play))',
icon: playIcon,
},
{
name: 'Self Care',
color: 'hsl(var(--clr-care))',
icon: careIcon,
},
{
name: 'Social',
color: 'hsl(var(--clr-social))',
icon: socialIcon,
},
{
name: 'Study',
color: 'hsl(var(--clr-study))',
icon: studyIcon,
},
{
name: 'Work',
color: 'hsl(var(--clr-work))',
icon: workIcon,
},
];
function App() {
const [selectedTime, setSelectedTime] = useState(2);
return (
<div className="app bg-dark">
<div className="main__container grid">
<div className="side__card-container">
<div className="side__card__top flex">
<div className="side__card__top__pb-container">
<img
src={pbImage}
alt="pb"
className="side__card__top__pb-image pb-image"
/>
</div>
<div className="side__card__top__person-container">
<p className="fs-600 text-accent">Report for</p>
<h2 className="fs-800 text-white">Jeremy Robson</h2>
</div>
</div>
<div className="side__card__bottom">
<div>Daily</div>
<div>Weekly</div>
<div>Monthly</div>
</div>
</div>
{cards.map((card, _index) => (
<Card
key={_index}
name={card.name}
color={card.color}
icon={card.icon}
currentTime={selectedTime}
/>
))}
</div>
</div>
);
}
export default App;
As I mentioned in the comments, states are updated asynchronously. So, you should be careful when you use a state immediately after setting its value. In your case, you shouldn’t use the data state because you are not sure that it has a value. Try changing to this.
useEffect(() => {
const fetchData = async () => {
const result = await axios.get('data.json');
const filteredData = result.data.filter((item) => {
return item.title === name;
})
setData(filteredData);
// make sure result data isn’t empty
setDaily(result.data[0].timeframes.daily);
setWeekly(result.data[0].timeframes.weekly);
setMonthly(result.data[0].timeframes.monthly);
};
fetchData();
}, []); // "[]" makes the useEffect callback only run after the first render
So im building a nuxt application, and I want to the Page to scroll down to a specific element, after I clicked a button.
The button and the element are in two different components, but on the same Page.
So I read that this is a problem in Nuxt.js and you have to create a specific file to make it work.
I created the folder called app and in app I created the file router.scrollBehavior.js the code of the file it the following
export default async function (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
const findEl = (hash, x) => {
return document.querySelector(hash) ||
new Promise((resolve, reject) => {
if (x > 50) {
return resolve()
}
setTimeout(() => { resolve(findEl(hash, ++x || 1)) }, 100)
})
}
if (to.hash) {
const el = await findEl(to.hash)
if ('scrollBehavior' in document.documentElement.style) {
return window.scrollTo({ top: el.offsetTop, behavior: 'smooth' })
} else {
return window.scrollTo(0, el.offsetTop)
}
}
return { x: 0, y: 0 }
}
My button is in the hero file, i created a click function called goto()
<template>
<section class="hero-section">
<div class="left">
<header id="hero-text" class="hero-header">
<h1 #click="goto()">Web Development <br> Web Design</h1>
<p>Hi, ich bin Alex. Ich designe und programmiere moderne, kreative und schnelle Webseiten. Umgesetzt mit den neusten Technologien.</p>
</header>
<UiButtonApp
id="hero-btn"
text="Schau dir meine Arbeit an!"
/>
</div>
<div class="right">
<img id="hero-img" src="~/assets/img/hero-img.jpg" alt="hero-img">
<div id="hero-object"></div>
<img class="dots" id="hero-dots" src="~/assets/img/dots.png" alt="logo">
</div>
</section>
</template>
<script>
import { gsap } from "gsap";
export default {
data(){
return{
}
},
methods: {
goto() {
//what code to put here
}
}
}
How can I now call the function? And make it work?
If you want to use router.scrollBehavior.js. Set a router action and send it to a especific hash:
methods: {
goto() {
this.$router.replace({ name: this.$route.name, hash: '#example' });
}
}
Don`t forget to set id in the component to go.
<div id="example">
</div>
I am working on a big project and I got an error.
It's a popup window.
I added it inside my app js file.
Before I added, everything went well, but now I got an error.😭
If anyone knows what my problem is, please let me know.
It should look like this:
This is my code, I copied it from an HTML, CSS, and JS tutorial:
import React from 'react';
import "./Popup.styles.css";
function Popup() {
const openModalButtons = document.querySelectorAll('[data-modal-target]')
const closeModalButtons = document.querySelectorAll('[data-close-button]')
const overlay = document.getElementById('overlay')
openModalButtons.forEach(button => {
button.addEventListener('click', () => {
const modal = document.querySelector(button.dataset.modalTarget)
openModal(modal)
})
})
overlay.addEventListener('click', () => {
const modals = document.querySelectorAll('.modal.active')
modals.forEach(modal => {
closeModal(modal)
})
})
closeModalButtons.forEach(button => {
button.addEventListener('click', () => {
const modal = button.closest('.modal')
closeModal(modal)
})
})
function openModal(modal) {
if (modal == null) return
modal.classList.add('active')
overlay.classList.add('active')
}
function closeModal(modal) {
if (modal == null) return
modal.classList.remove('active')
overlay.classList.remove('active')
}
return (
<div>
<button data-modal-target="#modal">Open Modal</button>
<div class="modal" id="modal">
<div class="modal-header">
<div class="title">Example Modal</div>
<button data-close-button class="close-button">×</button>
</div>
<div class="modal-body">
Lorem ipsum...
</div>
</div>
</div>
)
}
export default Popup
And this is the error I got:
TypeError: null is not an object (evaluating 'overlay.addEventListener')
Popup
src/Popup.js:17
14 | })
15 | })
16 |
> 17 | overlay.addEventListener('click', () => {
| ^ 18 | const modals = document.querySelectorAll('.modal.active')
19 | modals.forEach(modal => {
20 | closeModal(modal)
Please answer, you won't hurt my feelings.
Thank you in advance!
Emiel
I wish to place a list of posts on my home page instead of having to create a seperate dynamic page. This is my gatsby-node.js file
// DYNAMICALLY CREATE PAGES FOR EACH POST
module.exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions;
const postTemplate = path.resolve('src/templates/news.js');
const postResult = await graphql(`
query {
allContentfulPost {
edges {
node {
slug
}
}
}
}
`);
// Handle errors
if (postResult.errors) {
reporter.panicOnBuild('Error while running GraphQL query.');
return;
}
// Create the pages for each markdown file
postResult.data.allContentfulPost.edges.forEach(({ node }) => {
createPage({
component: postTemplate,
path: `/news/${node.slug}`,
context: {
slug: node.slug,
},
});
});
// PAGINATION FOR BLOG POSTS
const postsResult = await graphql(`
{
allContentfulPost(sort: { fields: date, order: DESC }, limit: 1000) {
edges {
node {
slug
}
}
}
}
`);
if (postsResult.errors) {
reporter.panicOnBuild('Error while running GraphQL query.');
return;
}
// Create blog-list pages
const posts = postsResult.data.allContentfulPost.edges;
const postsPerPage = 12;
const postNumPages = Math.ceil(posts.length / postsPerPage);
Array.from({ length: postNumPages }).forEach((_, i) => {
createPage({
path: i === 0 ? '/' : `/news/${i + 1}`,
component: path.resolve('./src/templates/news-list.js'),
context: {
limit: postsPerPage,
skip: i * postsPerPage,
postNumPages,
currentPage: i + 1,
},
});
});
};
And this is my news-list.js file
import React from 'react';
import { Link, graphql } from 'gatsby';
import Layout from '../components/layout';
import SEO from '../components/seo';
export const query = graphql`
query ($skip: Int!, $limit: Int!) {
allContentfulPost(sort: { fields: date, order: DESC }, limit: $limit, skip: $skip) {
edges {
node {
title
slug
date(formatString: "MMMM Do, YYYY")
}
}
}
}
`;
const NewList = (props) => {
// const { postNumPages } = props.pageContext;
const posts = props.data.allContentfulPost.edges;
return (
<Layout>
<SEO title='News' />
{posts.map(({ node }) => {
const title = node.title || node.slug;
return (
<div className='container mx-auto prose prose-lg'>
<div className='mb-2'>
<Link to={`/posts/${node.slug}`}>
<h3 className='underline font-sans mb-1'>{title}</h3>
</Link>
<div className='flex items-center justify-between'>
<span className='font-mono text-sm'>{node.date}</span>
</div>
</div>
</div>
);
})}
</Layout>
);
};
export default NewList;
I have tried to import the above news-list.js as component from my templates folder into my index.js folder. However I am getting the Error:
TypeError: Cannot read property 'allContentfulPost' of undefined
But if i add path: i === 0 ? '/news' : /news/${i + 1}, into my node file and go to localhost/news i get the list of posts.
But I want them on the home page.. So i thought If I was to just have / it would work turns out no.
How can i get the posts that are listed at LH/news to be displayed on my homepage instead.
Update
New Component after latest answer
import React from 'react';
import { useStaticQuery, graphql, Link } from 'gatsby';
import Layout from '../components/layout';
// import News from '../components/news';
// import NewsList from '../templates/news-list';
export const query = graphql`
{
allContentfulPost(sort: { fields: date, order: DESC }, limit: 1000) {
edges {
node {
title
slug
date(formatString: "MMMM Do, YYYY")
}
}
}
}
`;
const Index = ({ data }) => {
const { site } = useStaticQuery(
graphql`
query {
site {
siteMetadata {
companyname
}
}
}
`
);
return (
<Layout>
<section className='c-mt-10'>
<div className=''>
<div className='font-mono md:flex md:justify-between'>
<div className='mb-5'>
<a href={`mailto:hello#${site.siteMetadata.companyname}.co.uk`}>
hello#pfb{site.siteMetadata.companyname}.co.uk
</a>
<br />
<br />
<tel>+44 020 3925 6054</tel>
</div>
<a
href='https://www.google.com/maps/place/Warnford+Court,+29+Throgmorton+St,+London+EC2N+2AT/#51.5154096,-0.0890419,17z/data=!3m1!4b1!4m5!3m4!1s0x48761cacb440b98d:0x9742679143333ff!8m2!3d51.5154096!4d-0.0868479'
target='_blank'
rel='noreferrer'>
<address className='text-right'>
Warnford Court
<br />
29 Throgmorton Street
<br /> London, EC2N 2AT
</address>
</a>
</div>
</div>
<div>
<h2>Company News</h2>
<ul>
{data.allContentfulPost.edges.map(({ node }) => (
<li key={node.title}>
<Link to={node.slug}>{node.title}</Link>
</li>
))}
</ul>
</div>
</section>
</Layout>
);
};
export default Index;
I think you are mixing a lot of concepts.
One thing is the gatsby-node.js queries, useful to create dynamic pages based on dynamic data (from Contentful CMS in your case) based on a parameter (slug in your case).
Another thing is page queries, a way of retrieving data in a top-level components (pages or templates, not components).
If you want to list all your post in your homepage, you just need to create a GraphQL query and loop through the results just like:
const IndexPage = ({ data }) => {
return <Layout>
<ul>
{data.allContentfulPost.edges.map(({node})=> <li key={node.title}><Link to={node.slug}>{title}</Link></li>)}
</ul>
</Layout>
}
export const query = graphql`
{
allContentfulPost(sort: { fields: date, order: DESC }, limit: 1000) {
edges {
node {
title
slug
date(formatString: "MMMM Do, YYYY")
}
}
}
}
`;
When using page queries, your data is stored inside props.data so you can destructure them directly into data.
In your case, you were importing a template inside a page, which doesn't make much sense because you don't have, among other things, the query.
i am making Post system with React on my Rails project.
But when i try to render Posts in my Home page i get this error:
React::ServerRendering::PrerenderError in HomeController#index
Encountered error "TypeError: Cannot call method 'map' of undefined" when prerendering PostsList with {"posts":null} React.createClass.render ((execjs):19669:38)
My code is the above:
posts_list.js.jsx:
var PostsList = React.createClass({
getInitialState: function() {
return { posts: this.props.initialPosts };
},
render: function() {
var posts = this.state.posts.map(function(post) {
return <Post key={post.id} post={post} />;
});
return (
<div className="posts">
{posts}
</div>
);
}
});
post.js.jsx
var Post = React.createClass({
render: function() {
return (
<div className="post">
<PostTitle post={this.props.post} />
<PostBody post={this.props.post} />
</div>
);
}
});
var PostTitle = React.createClass({
render: function() {
return (
<div className="post-header">
<h2>{this.props.post.title}</h2>
<div className="post-meta">
By {this.props.post.user_id} - {this.props.post.created_at}
</div>
</div>
);
}
});
var PostBody = React.createClass({
render: function() {
return (
<div className="post-contents">
{this.props.post.body}
</div>
);
}
});
and index.html.erb
<div class="container-fluid">
<div class="col-md-8">
<h3> Posts Feed </h3>
<%= react_component('PostsList', {initialPosts: #posts }, {prerender: true}) %>
</div>
</div>
post_controller.rb
def index
#posts = Post.all
#user = current_user
end
when i use {initialPosts: Post.All } its works, but when i use with #posts , its not
any ideas why happening that? i have done posts into the site and #posts is not empty.
Ok i found what was wrong, i was rendering react in home layout, but the #posts was defined in posts controller instead of home controller.