Gatsby Dynamic Routing URL for Generating Layouts - reactjs

So I generate conditional layouts with this code:
exports.onCreatePage = ({ page, actions }) => {
const { createPage } = actions;
if (page.path.match(/about/)) {
page.context.layout = "special";
createPage(page);
}
if (page.path.match(/projects/)) {
page.context.layout = "projectsPage";
createPage(page);
}
};
I want to change the page.path.mathch(/projects/TO ALL PROJECT SLUGS/) but I can't write the correct syntax for the path.
Does anyone know how to get all the paths after the /projects/ ?
This is the full gatsby.node.js
const path = require("path");
const { createFilePath } = require(`gatsby-source-filesystem`);
exports.onCreatePage = ({ page, actions }) => {
const { createPage } = actions;
if (page.path.match(/about/)) {
page.context.layout = "special";
createPage(page);
}
if (page.path.match(/projects\/([^\/]+$)/)) {
page.context.layout = "projectsPage";
createPage(page);
}
};
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions;
if (node.internal.type === `MarkdownRemark`) {
const slug = createFilePath({ node, getNode, basePath: `pages` });
createNodeField({
node,
name: `slug`,
value: slug,
});
}
};
exports.createPages = async function ({ graphql, actions }) {
const { data } = await graphql(`
query Projects {
allMarkdownRemark {
nodes {
frontmatter {
slug
}
}
}
}
`);
data.allMarkdownRemark.nodes.forEach((node) => {
const slug = node.frontmatter.slug;
actions.createPage({
path: "/projects/" + slug,
component: path.resolve("./src/templates/project-details.js"),
context: { slug: slug },
});
});
};
And this is my template:
import React from "react";
import { AnimatePresence, motion } from "framer-motion";
import Navbar from "../components/Navbar/Navbar";
import Footer from "../components/Footer/Footer";
import FooterAbout from "../components/Footer/FooterAbout";
const duration = 0.5;
const variants = {
initial: {
opacity: 0,
},
enter: {
opacity: 1,
transition: {
duration: duration,
delay: duration,
when: "beforeChildren",
},
},
exit: {
opacity: 0,
transition: { duration: duration },
},
};
export const Layout = ({ children, location, pageContext }) => {
if (pageContext.layout === "projectsPage") {
return (
<main className="bg-black ">
<AnimatePresence>
<motion.main
key={location.pathname}
variants={variants}
initial="initial"
animate="enter"
exit="exit"
className="opacity-loader"
>
{children}
</motion.main>
</AnimatePresence>
</main>
);
}
if (pageContext.layout === "special") {
return (
<main className="bg-black ">
<Navbar />
<AnimatePresence>
<motion.main
key={location.pathname}
variants={variants}
initial="initial"
animate="enter"
exit="exit"
className="opacity-loader"
>
{children}
</motion.main>
</AnimatePresence>
<FooterAbout />
</main>
);
}
return (
<main className="bg-black ">
<Navbar />
<AnimatePresence>
<motion.main
key={location.pathname}
variants={variants}
initial="initial"
animate="enter"
exit="exit"
className="opacity-loader"
>
{children}
</motion.main>
</AnimatePresence>
<Footer />
</main>
);
};
export default Layout;
It seems like I am missing something, it accepts the (/projects/) path but not (/projects/([^/]+$)/).
To make it more clear I only want to disable the layout in the subdirectory pages of projects not in the /projects/ page.

You can use the following regular expression:
projects\/([^\/]+$)
This will match everything after /projects/. So:
if (page.path.match(/projects\/([^\/]+$)/)) {
page.context.layout = "projectsPage";
createPage(page);
}
I've added a sandbox to test all scenarios: https://regex101.com/r/eQRJb4/1
Alternatively, you can try gatsby-plugin-create-client-paths what makes exactly the same job automatically. In your case:
{
resolve: `gatsby-plugin-create-client-paths`,
options: { prefixes: [`/projects/*`] },
},
You can achieve the same result more easily like:
data.allMarkdownRemark.nodes.forEach((node) => {
const slug = node.frontmatter.slug;
actions.createPage({
path: "/projects/" + slug,
component: path.resolve("./src/templates/project-details.js"),
context: { slug: slug, layout: "projectsPage" },
});
});

Related

React: How update children component hidden to show?

I have a problem that children component does not update hidden status when project is selected, it should then display all the the tasks included in selected projects. How ever when getTasks is done and it updates hidden state to false and it passes state to children component props but children component never reintialize select component and remains hidden. What I need to change to make my selectbox class RtSelect to display hidden state changes?
My master component:
import React, { useState, useEffect, useRef } from 'react';
import RtSelect from './RtSelect';
import api, { route } from "#forge/api";
function Projects() {
const projRef = useRef();
const taskRef = useRef();
const [projects, setProjects] = useState(undefined)
const [tasks, setTasks] = useState(undefined)
const [projectid, setProjectid] = useState(undefined)
const [taskid, setTaskid] = useState(undefined)
const [hidden, setHidden] = useState(true)
//haetaan atlasiansita projectit array
useEffect(() => {
let loadedProject = true;
// declare the async data fetching function
const fetchProjects = async () => {
// get the data from the api
const response = await api.asUser().requestJira(route`/rest/api/3/project`, {
headers: {
'Accept': 'application/json'
}
});
const data = await response.json();
//Mapataa hausta tarvittavat tiedot
const result = data.map(function (item) {
console.log('test');
return [
{
label: item.name,
value: item.id,
avatar: item.avatarUrls['16x16']
}
]
})
// set state with the result if `isSubscribed` is true
if (loadedProject) {
setProjects(result);
}
}
//asetetaan state selectbox muutokselle
// call the function
fetchProjects()
// make sure to catch any error
.catch(console.error);;
// cancel any future `setData`
return () => loadedProject = false;
}, [param])
const getTasks = async (p) => {
// get the data from the api
const response = await api.asUser().requestJira(route`/rest/api/3/issuetype/project?projectId={p}`, {
headers: {
'Accept': 'application/json'
}
});
const data = await response.json();
//Mapataa hausta tarvittavat tiedot
const result = data.map(function (item) {
console.log('test');
return [
{
value: item.id,
label: item.description,
avatar: item.iconUrl
}
]
})
setTasks(result)
setHidden(false)
}
useEffect(() => {
projRef.current.addEventListener("onChange", (e) => {
setProjectid(e.target.value)
console.log("Project select boxin arvo on: " + e.target.value);
getTasks(projectid)
});
});
useEffect(() => {
taskRef.current.addEventListener("onChange", (e) => {
setTaskid(e.target.value)
console.log("Select task boxin arvo on: " + e.target.value);
});
});
return (
<div>
<div className='projects'>
<RtSelect info="Choose project:" options={projects} hidden={false} ref={projRef} />
</div>
<div className='tasks'>
<RtSelect info="Choose Task:" options={tasks} hidden={hidden} ref={taskRef} />
</div>
</div>
);
}
export default Projects
Here is my RtSelect class code:
import React from "react";
import Select from "react-select";
class RtSelect extends React.Component {
state = {
info: this.props.info,
options: this.props.options,
hidden: this.props.hidden,
menuIsOpen: '',
menuWidth: "",
IsCalculatingWidth: ''
};
constructor(props) {
super(props);
this.selectRef = props.ref
this.onMenuOpen = this.onMenuOpen.bind(this);
this.setData = this.setData.bind(this);
}
componentDidMount() {
if (!this.state.menuWidth && !this.state.isCalculatingWidth) {
setTimeout(() => {
this.setState({IsCalculatingWidth: true});
// setIsOpen doesn't trigger onOpenMenu, so calling internal method
this.selectRef.current.select.openMenu();
this.setState({menuIsOpen: true});
}, 1);
}
}
onMenuOpen() {
if (!this.state.menuWidth && this.state.IsCalculatingWidth) {
setTimeout(() => {
const width = this.selectRef.current.select.menuListRef.getBoundingClientRect()
.width;
this.setState({menuWidth: width});
this.setState({IsCalculatingWidth: false});
// setting isMenuOpen to undefined and closing menu
this.selectRef.current.select.onMenuClose();
this.setState({menuIsOpen: undefined});
}, 1);
}
}
styles = {
menu: (css) => ({
...css,
width: "auto",
...(this.state.IsCalculatingWidth && { height: 0, visibility: "hidden" })
}),
control: (css) => ({ ...css, display: "inline-flex " }),
valueContainer: (css) => ({
...css,
...(this.state.menuWidth && { width: this.state.menuWidth })
})
};
setData (props) {
if (props.info) {
this.setState({
info: props.info
})
}
if (props.options) {
this.setState({
options: props.options
})
}
if (props.hidden) {
this.setState({
hidden: props.hidden
})
}
}
render () {
return (
<div style={{ display: "flex" }}>
<div style={{ margin: "8px" }}>{this.state.info}</div>
<div style={{minWidth: "200px"}}>
<Select
ref={this.selectRef}
onMenuOpen={this.onMenuOpen}
options={this.state.options}
menuIsOpen={this.state.menuIsOpen}
styles={this.styles}
isDisabled={this.state.hidden}
formatOptionLabel={(options) => (
<div className="select-option" style={{ display: "flex", menuWidth: "200px"}}>
<div style={{ display: "inline", verticalAlign: "center" }}>
<img src={options.avatar} width="30px" alt="Avatar" />
</div>
<div style={{ display: "inline", marginLeft: "10px" }}>
<span>{options.label}</span>
</div>
</div>
)}
/>
</div>
</div>
);
}
}
export default RtSelect;
Ok I found from other examples that I can use the ref to acces child method so here is they way to update component:
useEffect(() => {
projRef.current.addEventListener("onChange", (e) => {
setProjectid(e.target.value)
console.log("Project select boxin arvo on: " + e.target.value);
getTasks(projectid)
//Using RtSelect taskRef to locate children component method to update component
taskRef.current.setData({hidden: false})
});
});

How can I change my data fetching strategy to avoid stale data on initial dynamic route renders?

I'm building a Next.js app which allows users to create and view multiple Kanban boards. There are a couple of different ways that a user can view their different boards:
On the Home page of the app, users see a list of boards that they can click on.
The main navigation menu has a list of boards users can click on.
Both use Next.js Link components.
Clicking the links loads the following dynamic page: src/pages/board/[boardId].js The [boardId].js page fetches the board data using getServerSideProps(). An effect fires on route changes, which updates the redux store. Finally, the Board component uses a useSelector() hook to pull the data out of Redux and render it.
The problem I'm experiencing is that if I click back and forth between different boards, I see a brief flash of the previous board's data before the current board data loads. I am hoping someone can suggest a change I could make to my approach to alleviate this issue.
Source code:
// src/pages/board/[boardId].js
import React, { useEffect } from 'react'
import { useDispatch } from 'react-redux'
import Board from 'Components/Board/Board'
import { useRouter } from 'next/router'
import { hydrateTasks } from 'Redux/Reducers/TaskSlice'
import { unstable_getServerSession } from 'next-auth/next'
import { authOptions } from 'pages/api/auth/[...nextauth]'
import prisma from 'Utilities/PrismaClient'
const BoardPage = ({ board, tasks }) => {
const router = useRouter()
const dispatch = useDispatch()
useEffect(() => {
dispatch(hydrateTasks({ board, tasks }))
}, [router])
return (
<Board />
)
}
export async function getServerSideProps ({ query, req, res }) {
const session = await unstable_getServerSession(req, res, authOptions)
if (!session) {
return {
redirect: {
destination: '/signin',
permanent: false,
},
}
}
const { boardId } = query
const boardQuery = prisma.board.findUnique({
where: {
id: boardId
},
select: {
name: true,
description: true,
id: true,
TO_DO: true,
IN_PROGRESS: true,
DONE: true
}
})
const taskQuery = prisma.task.findMany({
where: {
board: boardId
},
select: {
id: true,
title: true,
description: true,
status: true,
board: true
}
})
try {
const [board, tasks] = await prisma.$transaction([boardQuery, taskQuery])
return { props: { board, tasks } }
} catch (error) {
console.log(error)
return { props: { board: {}, tasks: [] } }
}
}
export default BoardPage
// src/Components/Board/Board.js
import { useEffect } from 'react'
import { useStyletron } from 'baseui'
import Column from 'Components/Column/Column'
import ErrorBoundary from 'Components/ErrorBoundary/ErrorBoundary'
import useExpiringBoolean from 'Hooks/useExpiringBoolean'
import { DragDropContext } from 'react-beautiful-dnd'
import Confetti from 'react-confetti'
import { useDispatch, useSelector } from 'react-redux'
import useWindowSize from 'react-use/lib/useWindowSize'
import { moveTask } from 'Redux/Reducers/TaskSlice'
import { handleDragEnd } from './BoardUtilities'
import { StyledBoardMain } from './style'
const Board = () => {
const [css, theme] = useStyletron()
const dispatch = useDispatch()
useEffect(() => {
document.querySelector('body').style.background = theme.colors.backgroundPrimary
}, [theme])
// get data from Redux
const { boardDescription, boardName, columnOrder, columns, tasks } = useSelector(state => state?.task)
// set up a boolean and a trigger to control "done"" animation
const { boolean: showDone, useTrigger: doneUseTrigger } = useExpiringBoolean({ defaultState: false })
const doneTrigger = doneUseTrigger({ duration: 4000 })
// get width and height for confetti animation
const { width, height } = useWindowSize()
// curry the drag end handler for the drag and drop UI
const curriedDragEnd = handleDragEnd({ dispatch, action: moveTask, handleOnDone: doneTrigger })
return (
<ErrorBoundary>
<DragDropContext onDragEnd={curriedDragEnd}>
<div className={css({
marginLeft: '46px',
marginTop: '16px',
fontFamily: 'Roboto',
display: 'flex',
alignItems: 'baseline'
})}>
<h1 className={css({ fontSize: '22px', color: theme.colors.primary })}>{boardName}</h1>
{boardDescription &&
<p className={css({ marginLeft: '10px', color: theme.colors.primary })}>{boardDescription}</p>
}
</div>
<StyledBoardMain>
{columnOrder.map(columnKey => {
const column = columns[columnKey]
const tasksArray = column.taskIds.map(taskId => tasks[taskId])
return (
<Column
column={columnKey}
key={`COLUMN_${columnKey}`}
tasks={tasksArray}
title={column.title}
status={column.status}
/>
)
})}
</StyledBoardMain>
</DragDropContext>
{showDone && <Confetti
width={width}
height={height}
/>}
</ErrorBoundary>
)
}
export default Board
// src/pages/index.tsx
import React, {PropsWithChildren} from 'react'
import {useSelector} from "react-redux";
import {authOptions} from 'pages/api/auth/[...nextauth]'
import {unstable_getServerSession} from "next-auth/next"
import CreateBoardModal from 'Components/Modals/CreateBoard/CreateBoard'
import Link from 'next/link'
import {useStyletron} from "baseui";
const Index: React.FC = (props: PropsWithChildren<any>) => {
const {board: boards} = useSelector(state => state)
const [css, theme] = useStyletron()
return boards ? (
<>
<div style={{marginLeft: '46px', fontFamily: 'Roboto', width: '600px'}}>
<h1 className={css({fontSize: '22px'})}>Boards</h1>
{boards.map(({name, description, id}) => (
<Link href="/board/[boardId]" as={`/board/${id}`} key={id}>
<div className={css({
padding: '20px',
marginBottom: '20px',
borderRadius: '6px',
background: theme.colors.postItYellow,
cursor: 'pointer'
})}>
<h2 className={css({fontSize: '20px'})}>
<a className={css({color: theme.colors.primary, width: '100%', display: 'block'})}>
{name}
</a>
</h2>
<p>{description}</p>
</div>
</Link>
))}
</div>
</>
) : (
<>
<h1>Let's get started</h1>
<button>Create a board</button>
</>
)
}
export async function getServerSideProps(context) {
const session = await unstable_getServerSession(context.req, context.res, authOptions)
if (!session) {
return {
redirect: {
destination: '/signin',
permanent: false,
},
}
}
return {props: {session}}
}
export default Index
It looks like there's only ever one board in the redux. You could instead use a namespace so that you don't have to keep swapping different data in and out of the store.
type StoreSlice = {
[boardId: string]: Board;
}
Then the "brief flash" that you will see will either be the previous data for the correct board, or nothing if it has not yet been fetched.

Navigate around navigation bar using key event in react js

Hi my problem is I have done a react js web page I have a navigation bar and my default menu is home and I have 4 more menus beside home menu and now I want to apply keyevent to navigate to right and left of the navigation bar amd when i press up and down arrow keys the focus should move to down section of the navigation bar i mean to next div section has i mentioned in home.js the next div is banner of web page and next to banner is row of images(Tvshows component) and so on to navigate through all the div in the page.
Menu.js
import React, { useEffect, useState } from "react";
import { Menudata } from "./Menudata";
import ListItem from "./ListItem";
const useKeyPress = function (targetKey) {
const [keyPressed, setKeyPressed] = useState(false);
function downHandler({ key }) {
if (key === targetKey) {
setKeyPressed(true);
}
}
const upHandler = ({ key }) => {
if (key === targetKey) {
setKeyPressed(false);
}
};
React.useEffect(() => {
window.addEventListener("keydown", downHandler);
window.addEventListener("keyup", upHandler);
return () => {
window.removeEventListener("keydown", downHandler);
window.removeEventListener("keyup", upHandler);
};
});
return keyPressed;
};
const Menu = () => {
const [selected, setSelected] = useState(undefined);
const rightPress = useKeyPress("ArrowRight");
const leftPress = useKeyPress("ArrowLeft");
const [cursor, setCursor] = useState(0);
const [hovered, setHovered] = useState(undefined);
useEffect(() => {
if (Menudata.length && rightPress) {
setCursor((prevState) =>
prevState < Menudata.length - 1 ? prevState + 1 : prevState
);
}
}, [rightPress]);
useEffect(() => {
if (Menudata.length && leftPress) {
setCursor((prevState) => (prevState > 0 ? prevState - 1 : prevState));
}
}, [leftPress]);
useEffect(() => {
if (Menudata.length && hovered) {
setCursor(Menudata.indexOf(hovered));
}
}, [hovered]);
return (
<div className="Menu">
{Menudata.map((item, i) => (
<ListItem
key={item.id}
active={i === cursor}
item={item}
setSelected={setSelected}
setHovered={setHovered}
/>
))}
</div>
);
};
export default Menu;
Menudata.js
export const Menudata = [
{
name:"Home"
},
{
name:"TVshows"
},
{
name:"Originals"
},
{
name:"Movies"
},
{
name:"Sports"
},
{
name:"Premium"
}
]
Home.js
import React from "react";
import Tvshows from "./Tvshows";
import Originals from "./Originals";
const Home= () => {
const home = [
{ src: './images/tamil.jpg' },
{ src: './images/telugu.jpg' },
{ src: './images/malayam.jpg' },
{ src: './images/marathi.jpg' },
{ src: './images/hindi.jpg' },
{ src: './images/english.jpg' },
{ src: './images/bengali.jpg' },
{ src: './images/kannada.jpg' },
];
const largeimg=[
{ src: 'images/Undekhi_web_tray_dated_23march_rev (1).jpg' },
];
return (
<div className="row">
<div className="banner">
<div className="banner__contents">
<h1 className="banner__title"><h1>LIVE</h1> STREAMING</h1>
</div>
</div>
<div>
<Tvshows/>
</div>
<div className="row_images" >
<h1>Watch in your language</h1>
{
home.map((index) => <img src={index.src} alt="" height="180" width="155"></img>)
}
</div>
<div >
<Originals/>
</div>
<div className="row__largeimage" >
{
largeimg.map((index) => <img src={index.src} alt="" ></img>)
}
</div>
</div>
)
}
export default Home;

React.js error: The service worker navigation preload request was cancelled before 'preloadResponse' settled

I have an issue with my React application (with Redux Saga), I'm getting the console error:
The service worker navigation preload request was cancelled before 'preloadResponse' settled. If you intend to use 'preloadResponse', use waitUntil() or respondWith() to wait for the promise to settle.
I see this error on the console only on Chrome, not in Firefox or Edge.
This error does not affect my application.
The following steps reproduce the error:
1. Main page upload.
2. Go to movie details page.
3. Go back to main page.
Main.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { mainActions } from '../../store/actions/actions';
import './Main.scss';
import { MoviesList, SearchPanel } from '../../components';
const propTypes = {};
const defaultProps = {};
class Main extends Component {
constructor(props) {
super(props);
this.handleSearchTextChange = this.handleSearchTextChange.bind(this);
this.handleLoadMoreButtonClick = this.handleLoadMoreButtonClick.bind(this);
this.handleMovieClick = this.handleMovieClick.bind(this);
this.handleFavoriteMovieClick = this.handleFavoriteMovieClick.bind(this);
}
componentDidMount() {
this.handleComponentDidMount();
}
handleComponentDidMount() {
const { moviesList } = this.props;
if (!moviesList || moviesList.length <= 0) {
this.getMovies(null, false);
}
}
handleLoadMoreButtonClick() {
this.getMovies(null, false);
}
handleMovieClick(e) {
if (e.target.className === 'movie') {
this.props.history.push(`/details/${e.currentTarget.dataset.id}`);
}
}
handleSearchTextChange(e) {
const { pageNumber, favoriteMoviesList } = this.props;
this.props.onSearchTextChange({
searchText: e.target.value,
pageNumber: pageNumber,
favoriteMoviesList: favoriteMoviesList
});
}
handleFavoriteMovieClick(e) {
const { id, name, posterId } = e.currentTarget.dataset;
const { moviesList, favoriteMoviesList } = this.props;
this.props.onUpdateFavoriteMovies({
updatedMovie: { id: id, name: name, posterId: posterId },
favoriteMoviesList: favoriteMoviesList,
moviesList: moviesList
});
}
getMovies(updatedSearchText, isSearchChange) {
const { searchText, pageNumber, favoriteMoviesList } = this.props;
this.props.onLoadMovies({
pageNumber: pageNumber,
favoriteMoviesList: favoriteMoviesList,
updatedSearchText: isSearchChange ? updatedSearchText : searchText,
isSearchChange: isSearchChange
});
}
render() {
const { searchText, isLoadingMoreMovies, isPager, moviesList } = this.props;
return (
<div className="main-area">
<SearchPanel
searchText={searchText}
onSearchTextChange={this.handleSearchTextChange}
/>
<MoviesList
pageName='movies'
moviesList={moviesList}
isLoadingMoreMovies={isLoadingMoreMovies}
isPager={isPager}
onLoadMoreClick={this.handleLoadMoreButtonClick}
onMovieClick={this.handleMovieClick}
onFavoriteMovieClick={this.handleFavoriteMovieClick}
/>
</div>
);
}
}
Main.propTypes = propTypes;
Main.defaultProps = defaultProps;
const mapStateToProps = (state) => {
return {
searchText: state.main.searchText,
pageNumber: state.main.pageNumber,
isLoadingMoreMovies: state.main.isLoadingMoreMovies,
isPager: state.main.isPager,
moviesList: state.main.moviesList,
favoriteMoviesList: state.main.favoriteMoviesList
};
};
const mapDispatchToProps = (dispatch) => {
return {
onLoadMovies: (request) => dispatch(mainActions.loadMovies(request)),
onSearchTextChange: (request) => dispatch(mainActions.searchTextChange(request)),
onUpdateFavoriteMovies: (request) => dispatch(mainActions.updateFavoriteMovies(request))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Main);
Details.jsx
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { detailsActions, mainActions } from '../../store/actions/actions';
import './Details.scss';
import { ActorsList, ButtonClick, CrewsList, FeaturesList, PageTitle, ProductionsList, Rating, Trailer } from '../../components';
import movieUtils from '../../utils/movie.utils';
const propTypes = {};
const defaultProps = {};
class Details extends Component {
constructor(props) {
super(props);
this.handleBackClick = this.handleBackClick.bind(this);
this.handleFavoriteMovieClick = this.handleFavoriteMovieClick.bind(this);
this.isFavorite = false;
}
componentDidMount() {
this.handleComponentDidMount();
}
handleComponentDidMount() {
if (this.props.moviesList.length <= 0) {
this.handleBackClick();
return;
}
const movieId = this.props.match.params.id;
if (!movieId) {
this.handleBackClick();
return;
}
this.props.onLoadMovieDetails(movieId);
this.updateIsFavorite(movieId);
}
numberWithCommas(number) {
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
updateIsFavorite(movieId) {
this.isFavorite = this.props.favoriteMoviesList.findIndex(movie => parseInt(movie.id) === parseInt(movieId)) > -1;
}
handleBackClick() {
this.props.history.push(`/`);
}
handleFavoriteMovieClick() {
const { movie, moviesList, favoriteMoviesList } = this.props;
this.props.onUpdateFavoriteMovies({
updatedMovie: { id: movie.id, name: movie.title, posterId: movie.poster_path },
favoriteMoviesList: favoriteMoviesList,
moviesList: moviesList
});
this.updateIsFavorite(movie.id);
}
render() {
const { movie, youtubeKey, credits } = this.props;
if (!movie) {
return null;
}
const { adult, poster_path, budget, genres, homepage, imdb_id, original_language, original_title,
overview, popularity, production_companies, production_countries, release_date, revenue, runtime, spoken_languages,
status, tagline, title, video, vote_average, vote_count } = movie;
const genresText = genres.map(genre => genre.name).join(', ');
const countriesText = production_countries.map(country => country.name).join(', ');
const languagesText = spoken_languages.map(language => language.name).join(', ');
const featuresList = [
{ item: 'Release Date', value: release_date },
{ item: 'Budget', value: `$${this.numberWithCommas(budget)}` },
{ item: 'Revenue', value: `$${this.numberWithCommas(revenue)}` },
{ item: 'Length', value: `${runtime} minutes` },
{ item: 'Popularity', value: popularity },
{ item: 'Original Title', value: original_title },
{ item: 'For Adults', value: adult ? 'Yes' : 'No' },
{ item: 'Original Language', value: original_language },
{ item: 'Spoken Languages', value: languagesText },
{ item: 'Countries', value: countriesText },
{ item: 'Status', value: status },
{ item: 'Is Video', value: video ? 'Yes' : 'No' }
];
const linksList = [];
if (homepage) {
linksList.push({ id: 1, name: 'Homepage', url: homepage });
}
if (imdb_id) {
linksList.push({ id: 2, name: 'IMDB', url: `https://www.imdb.com/title/${imdb_id}` });
}
const actorsList = movieUtils.removeDuplicates(credits ? credits.cast ? credits.cast : null : null, 'name');
const crewsList = movieUtils.removeDuplicates(credits ? credits.crew ? credits.crew : null : null, 'name');
return (
<div>
<section className="details-area">
<PageTitle
pageName='details'
pageTitle='Details'
/>
<ul className="details-content">
<li className="details-left" style={{ backgroundImage: `url('https://image.tmdb.org/t/p/original${poster_path}')` }}></li>
<li className="details-right">
<h2>{title} ({release_date.substr(0, 4)})</h2>
<p className="genres">{genresText}</p>
<p className="description short">{tagline}</p>
<Rating
rating={vote_average}
votesCount={this.numberWithCommas(vote_count)}
/>
<p className="description full">{overview}</p>
<div className="extra">
<FeaturesList
featuresList={featuresList.slice(0, 5)}
linksList={null}
isFavorite={this.isFavorite}
onFavoriteMovieClick={this.handleFavoriteMovieClick}
/>
{youtubeKey && <Trailer
youtubeKey={youtubeKey}
/>}
</div>
</li>
<div className="extra-features">
<FeaturesList
featuresList={featuresList.slice(5, featuresList.length)}
linksList={linksList}
isFavorite={null}
onFavoriteMovieClick={null}
/>
<ProductionsList
productionsList={production_companies}
/>
</div>
</ul>
</section>
<section className="actors-area">
<PageTitle
pageName='actors'
pageTitle='Cast'
/>
<ActorsList
actorsList={actorsList}
/>
</section>
<section className="crew-area">
<PageTitle
pageName='crew'
pageTitle='Crew'
/>
<CrewsList
crewsList={crewsList}
/>
</section>
<ButtonClick
buttonText={'Back'}
buttonTitle={'Back'}
isLoading={false}
onClick={this.handleBackClick}
/>
</div>
);
}
}
Details.propTypes = propTypes;
Details.defaultProps = defaultProps;
const mapStateToProps = (state) => {
return {
movie: state.details.movie,
youtubeKey: state.details.youtubeKey,
credits: state.details.credits,
moviesList: state.main.moviesList,
favoriteMoviesList: state.main.favoriteMoviesList
};
};
const mapDispatchToProps = (dispatch) => {
return {
onLoadMovieDetails: (movieId) => dispatch(detailsActions.loadDetails(movieId)),
onUpdateFavoriteMovies: (request) => dispatch(mainActions.updateFavoriteMovies(request))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Details);
What I already looked in:
Getting The service worker navigation preload request was cancelled before 'preloadResponse' settled
https://learn.microsoft.com/en-us/answers/questions/108004/getting-the-service-worker-navigation-preload-requ.html
https://support.google.com/mail/thread/4055804?hl=en
https://love2dev.com/pwa/service-worker-preload/
I tried to put this on Details.jsx page, but it didn't work:
self.addEventListener('fetch', event => {
event.respondWith(async function () {
// Respond from the cache if we can
const cachedResponse = await caches.match(event.request);
if (cachedResponse) return cachedResponse; // Else, use the preloaded response, if it's there
const response = await event.preloadResponse;
if (response) return response; // Else try the network.
return fetch(event.request);
}());
});
self.addEventListener('activate', event => {
event.waitUntil(async function () {
// Feature-detect
if (self.registration.navigationPreload) { // Enable navigation preloads!
console.log('Enable navigation preloads!');
await self.registration.navigationPreload.enable();
} return;
})();
});
How can I solve this issue? Thanks.
Had same error, even my iframe wasn't loading..whatever video you are using from youtube use nocookie/embed in url. It's working for me.
Try changing https://www.youtube.com/watch?v=i8eBBG46H8A to
https://www.youtube-nocookie.com/embed/i8eBBG46H8A
Hope nocookie & embed helps..!!

How to add a class to an image by click?

How to add a class to an image if the flag is done: true? As I have not tried, the class is added to all images, and not to those with true...
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [avatarArr, setAvatarArr] = useState({
avatar: [
{
id: 1,
url: "https://starwars-visualguide.com/assets/img/characters/1.jpg"
},
{
id: 2,
url: "https://starwars-visualguide.com/assets/img/characters/2.jpg"
},
{
id: 3,
url: "https://starwars-visualguide.com/assets/img/characters/3.jpg"
}
]
});
const [classUser, setClassUser] = useState(null);
const [selectUser, setSelectUser] = useState(false);
const onAddClass = id => {
if (avatarArr.avatar.find(items => items.id === id)) {
const index = avatarArr.avatar.findIndex(items => items.id === id);
setAvatarArr([
...avatarArr.slice(0, index),
...avatarArr.slice(index + 1)
]);
} else {
setAvatarArr([...avatarArr, { done: true }]);
setSelectUser(avatarArr.avatar.map(items => items.done));
if (selectUser) {
setClassUser("active__user");
}
}
};
const blockCreate = () => {
return avatarArr.avatar.map(items => {
return (
<div key={items.id}>
<img
src={items.url}
alt="avatar"
width="150px"
onClick={() => onAddClass(items.done, items.id)}
className={selectUser ? classUser : null}
/>
</div>
);
});
};
return (
<div className="App">
<div>{blockCreate()}</div>
</div>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I'm trying to set true on click, to tell the user that the avatar that was clicked on is selected, and add some kind of styling class.
And if you click a second time, then true - becomes false, in short - the choice
Are you looking like this
export default function App() {
const [avatarArr, setAvatarArr] = useState({
avatar: [
{
id: 1,
url: "https://starwars-visualguide.com/assets/img/characters/1.jpg"
},
{
id: 2,
url: "https://starwars-visualguide.com/assets/img/characters/2.jpg"
},
{
id: 3,
url: "https://starwars-visualguide.com/assets/img/characters/3.jpg"
}
]
});
const [selectUser, setSelectUser] = useState(false);
const onAddClass = item => {
setSelectUser(item);
};
const blockCreate = () => {
return avatarArr.avatar.map(items => {
return (
<div key={items.id}>
<img
src={items.url}
alt="avatar"
width="150px"
onClick={() => onAddClass(items)}
className={selectUser.id === items.id ? "myClass" : ""}
/>
</div>
);
});
};
return (
<div className="App">
<div>{blockCreate()}</div>
</div>
);
}
Live working demo https://codesandbox.io/s/vigilant-almeida-169zj

Resources