Cannot read property 'urls' of undefined after refresh - javascript

I've created a photo app when the user clicks on the modal then the modal pops up with a new url with the photo id. However, when I refresh the page then it shows the error. I'm using unsplash api to retrieve the photos. how to remove this error when the user refreshes the page and show the modal instead which was there before the refresh?
sandbox
Listitem.js
import React, { useState } from "react";
import { Link, BrowserRouter as Router, Route } from "react-router-dom";
import ModalWrapper from "./ModalWrapper";
const ListItem = ({ photo }) => {
return (
<>
<Router>
<div key={photo.id} className="grid__item card">
<div className="card__body">
<Link to={{ pathname: `/${photo.id}`, state: photo }}>
<img src={photo.urls.small} alt="" />
</Link>
<Route path="/:photoId" component={ModalWrapper} />
</div>
</div>
</Router>
</>
);
};
export default ListItem;
ModalWrapper.js
import React from "react";
import Modal from "react-modal";
import { useHistory, useLocation } from "react-router-dom";
const customStyles = {
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)"
}
};
Modal.setAppElement("#root");
function ModalWrapper() {
const history = useHistory();
const location = useLocation();
const photo = location.state;
function downloadImage() {}
function close() {
history.push("/");
}
return (
<Modal isOpen={true} onRequestClose={close} style={customStyles}>
<img src={photo.urls.small} alt="" />
<div>
<button onClick={close} className="button">
Close
</button>
<button onClick={downloadImage()}>Download</button>
</div>
</Modal>
);
}
export default ModalWrapper;

It seem that useLocation() of "react-router-dom" module does not have any state when you first land on the page.
Maybe you have to first set it with the data from https://api.unsplash.com/photos${window.location.pathname}?client_id=your_clt_id and after that you can open the modal with the photo.urls.small
So you have to fill your application state before the modal is opened, I guess.
Anyway the error says
Warning: Can't perform a React state update on an unmounted component.
This is a no-op, but it indicates a memory leak in your application.
To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in App (at src/index.js:9)
in StrictMode (at src/index.js:8)
But I don't think that is a memory leak but rater, as I said above, that you are opening the modal before the app is loaded propery (ie the states filled)
UPDATE
Maybe a possible solution is to make a singleton of your App component and then use that singleton instance for retriving the photo in the instance.state.photos, instead of picking the location.state.
Anyway I don't like singletons, and I don't like React neither! It seems quite overkill for an app like yours, IMHO.
I also decided to explore codesandbox, and I found that you can fork it on github. Wow!
So I made a repo on my github in order to better explain what I'm talking about!
I think that this commit is clear enough

Related

Reload a page after the initial load with Next.js

I have a route called "./checkout" that renders embedded elements from Xola. The issue is I am using client side routing and the page needs a refresh to load the checkout page correctly (if not, Xola elements do not show up on the DOM 1). When I try to reload the page on the initial load I get an infinite reload loop. I can't use a href for specific reasons so I need to continue to use Next.js routing. Anyway I can go about this? EDIT: I have reached out to Xola support team for further assistance.
After refresh
checkout.js
import Head from "next/head";
import { useRouter } from "next/router";
import { Container, Button } from "#mui/material";
import { makeStyles } from "#mui/styles";
import { CheckoutCard } from "../components/layout/directory";
import useIsSsr from "#/config/useSsr";
function Checkout() {
const isSsr = useIsSsr();
const router = useRouter();
const classes = useStyles();
return (
<>
{isSsr ? null : window.location.reload()}
<Head>
<title>checkout</title>
</Head>
<Container className={classes.root}>
<Button
className={classes.btn}
onClick={router.back}
color="secondary"
variant={"contained"}
>
back
</Button>
<CheckoutCard />
</Container>
</>
);
}
const useStyles = makeStyles((theme) => ({
root: { marginTop: theme.spacing(10) },
btn: { marginBottom: theme.spacing(5) },
}));
export default Checkout;
CheckoutCard.js
function CheckoutCard() {
return (
<div
className="xola-embedded-checkout"
data-seller="5f3d889683cfdc77b119e592"
data-experience="5f3d8d80d6ba9c6b14748160"
data-version="2"
id="xola-checkout"
></div>
);
}
export default CheckoutCard;
Please add one more prop to CheckoutCard component calling in checkout.js.
You need to update
<CheckoutCard
url={`https://checkout.xola.com/index.html#seller/5f3d889683cfdc77b119e592/experiences/${
url && url.slice(1)
}?openExternal=true`}
/>
to
<CheckoutCard
url={`https://checkout.xola.com/index.html#seller/5f3d889683cfdc77b119e592/experiences/${
url && url.slice(1)
}?openExternal=true`}
key={new Date().getTime()}
/>
"key" prop is to identify the component and you are going to use external service ( like iframe, not sure correctly )
So in order to render the embedded elements from Xola, you should add "key" prop for CheckoutCard component calling.

React-Tooltip not displaying until page is refreshed

Bit of a weird one here. My website is virtually done, there is just one issue. I've implemented tooltips, but they only display once I refresh the page! Here is a GIF reproducing the issue:
https://i.imgur.com/NbHyN77.mp4
The package is from NPM, at the following link: https://www.npmjs.com/package/react-tooltip
I've went through their documentation, troubleshooting and issues reported on their github repo, but there is nothing describing my issue. The site is live at: https://ezekias1337.github.io/Burning-Crusade-Talent-Calculator/#/
Oddly enough, if I bookmark one of the routes and load it in a fresh tab, it loads the first time. The issue only happens when I select the component from my Icons.
I made sure to import ReactTooltip from "react-tooltip"; I also added at the bottom of each component, and in app.js. Adding the data-for attribute hasn't fixed the issue.
Here is the code of my App.js:
import ReactTooltip from 'react-tooltip';
class App extends Component {
render() {
return (
<div className="App">
<CustomNavbar />
<ClassSelector />
<FooterComponent />
<ReactTooltip
html={true}
/>
</div>
);
}
}
export default App;
Here is the code relevant to tooltips in each individual component:
a.) The image that has the tooltip (every image has unique tooltip)
<img
onMouseEnter={this.displayMouseOverlayInnerElement}
onMouseLeave={this.hideMouseOverlayInnerElement}
onMouseDown={() => {
this.talentClick();
this.toolTipFunction();
}}
onTouchEnd={this.talentClick}
className="talentHover"
src={overlayImage}
style={{ display: "none" }}
data-tip={Hunter[0].toolTip[0]}
id="1"
/>
b.) The bottom of the component
<ReactTooltip data-html="true" />
Any idea what I can do to fix this?
In case anyone else is having this issue, I have finally found the solution after hours of pulling my hair out.
I used the following function:
rebuildToolTip(){
ReactTooltip.rebuild();
}
Subsequently I added this function as an event handler for onLoad on the component being rendered.
<div
style={{ position: "relative" }}
onContextMenu={(e) => e.preventDefault()}
className="frame-wrapper"
id="Hunter"
onLoad={() => {
this.scrollComponentIntoView();
this.rebuildToolTip();
}}
>
Here is something that worked for me (https://github.com/wwayne/react-tooltip/issues/268), create a new component for tool tip and pass required details in props as below code.
import React from "react";
import ReactDOM from "react-dom";
import ReactTooltip from "react-tooltip";
// Create root level element for react-tooltips
const domNode = document.createElement('div');
document.body.appendChild(domNode);
// Wrapper component to portal react-tooltips
function BodyPortal ({ children }) {
return ReactDOM.createPortal(
children,
domNode
);
}
// Custom tooltip wrapper to ensure all tooltips get rendered into the portal
function CustomReactTooltip (props) {
return (
<BodyPortal>
<ReactTooltip
type="light"
effect="solid"
delayHide={50}
{...props}
/>
</BodyPortal>
);
}
export default CustomReactTooltip;

Reactjs: On clicking title display blog post contents

I'm trying to make a blogsite using MERN stack. So here I have my Blogs component which fetches all blogposts from db and display as shown below
here's the react code for the above pic
import { useEffect, useState } from "react";
import axios from 'axios';
import BlogDetails from "./BlogDetails";
const Blogs = () => {
const [blogPost, setPosts] = useState([])
useEffect(()=>{
const fetchBlog = async ()=>{
const blogsData = await axios('http://localhost:4000/blogs')
const blogsFetched = blogsData.data
console.log(blogsFetched);
setPosts(blogsFetched)
}
fetchBlog()
},[])
return (
<div className="blogs content">
<h2>All Blogs</h2>
{ blogPost.map((post)=>(
<div key={post._id}>
<a className="single" href={post._id}>
<h3 className="title" >{post.title}</h3>
<p className="snippet">{post.snippet}</p>
</a>
</div>
))
}
</div>
);
}
export default Blogs;
I want to show a particular blog post in detail when I click on its title from the above page.
On clicking the title, those details will be passed as props to another component named BlogDetails and will be rendered. The part where I'm stuck is routing to BlogDetails component with blogpost id. Is there any way in which on clicking the title, I can use Route to render BlogDetails component?
Please help
Assuming you're using react-router, you could do something like:
const history = useHistory();
.
.
.
<div onClick={() => history.push(`/blog/${post.id}`)}>
<h3 className="title">{post.title}</h3>
</div>
And then your router will render your component which you specified to be rendered for the /blog route. In that route, you can fetch the url using useLocation and then parse the id by splitting the url.
const { pathName } = useLocation(); // also from react-router
Alternatively (and more elegantly), you can push pieces of state when you do history.push, like:
...onClick={() => history.push(`/blog${post.id}`, { blogId: ${post.id}})}
Then again in your subsequently rendered component you use useLocation to get the state passed:
const { state } = useLocation();
yes you can use route for BlogDetail component
add BlogDetail link in BrowserRouter
<Route path={'url'} render={(props) => <BlogDetail/>}/>
In Blogs component use Link of react router dom and then use this link on title in render html like this
import { Link } from "react-router-dom";
{ blogPost.map((post)=>(
<div key={post._id}>
<a className="single" href={post._id}>
<Link to={{
pathname:'url', // this will be your url of BlogDetail
state: post //if you want post object detail in next component
}}>
<h3 className="title" >{post.title}</h3>
</Link>
<p className="snippet">{post.snippet}</p>
</a>
</div>
))
}

How to make page scroll automatically to components when rendered using React Hooks

I'm new to coding and been working with this simple portfolio page logic:
codesandbox
By using useRef and use effect hooks, I've got into a point in which the logic in child component scrolls the page to the most recent useRef value.
Now when the buttons are being pushed several times, the useRef value stops updating. For example, when the "things" button is pressed, it stays in that value and scrolls towards it with every button.
There's also a second problem in which the pages that are rendered on top of the homepage are being scrolled to top immediately without the scrollIntoView animation. Is there a way to lock a certain height when components are rendered on the top side?
Also, I'm aware of my bad habit of duplicating the same logic for every button and state. How could I work around this to make it more DRY?
I created one method to change state, please let me know if there will be any confusion. Here you can see example for scrolling part How to Scroll to Item in React.
import React, { useState, useRef, useEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import Portfolio from "./portfolio";
import Things from "./things";
import Contact from "./contact";
import About from "./about";
function App() {
const [showContent, setShowContent] = useState({
about: false,
contact: false,
portfolio: false,
things: false
});
const showRuntimeContent = modelName => {
if (modelName) {
setShowContent({[modelName]: !showContent[modelName]});
}
};
return (
<div className="home">
<div className="container">
<button onClick={() => showRuntimeContent("contact")}>Contacts</button>
<button onClick={() => showRuntimeContent("portfolio")}>Portfolio</button>
<button onClick={() => showRuntimeContent("about")}>About</button>
<button onClick={() => showRuntimeContent("things")}>Things</button>
<h1> HOME</h1>
</div>
<div>
{showContent.contact ? <Contact /> : null}
{showContent.portfolio ? <Portfolio /> : null}
{showContent.about ? <About /> : null}
{showContent.things ? <Things /> : null}
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

react: how to set state on page render

I'm working with react and I'm trying to set state on page render but it keeps throwing the below error.
---Error----
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
---Error----
Below is my code of the sidebar component. I'm using Context API for data management as shown in the code and I'm trying to set state of the role inside the showContext method with the value I'm getting through the Context API Consumer.
import React, { Component, PropTypes } from "react";
import { Menu, Icon } from "antd";
import { BrowserRouter as Router, Link, Route, Switch } from "react-router-dom";
import AdminPage from "../components/Admin/AdminPage";
import App from "../App";
import "../components/Login/LoginPage";
import { LoginContext } from "../context/LoginContext";
export default class MainMenu extends React.Component {
constructor(props) {
super(props);
this.state = {
roleValue: "admin",
status: false
};
this.showContext = this.showContext.bind(this);
}
showContext(context) {
let role = context;
if (this.state.roleValue == role) {
this.setState({
roleValue : "admin",
});
}
}
render() {
if (window.location.href == "http://localhost:8081/") {
var loginHeader =
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={["2"]}
selectedKeys={[location.pathname]}
>
{this.props.children}
<Menu.Item key="mastering">Mastering</Menu.Item>
</Menu>;
}
else {
if (this.state.roleValue == "general") {
var generalHeader1 =
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={["2"]}
selectedKeys={[location.pathname]}
>
{this.props.children}
<Menu.Item key="mastering">Mastering</Menu.Item>
<Menu.Item>
<Link to="/"> Logout</Link>
</Menu.Item>
<Menu.Item>
<Link to="/home">Home</Link>
</Menu.Item>
</Menu>;
}
else {
var generalHeader2 =
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={["2"]}
selectedKeys={[location.pathname]}
>
{this.props.children}
<Menu.Item key="mastering">Mastering</Menu.Item>
<Menu.Item>
<Link to="/"> Logout</Link>
</Menu.Item>
<Menu.Item>
<Link to="/admin">Admin</Link>
</Menu.Item>
<Menu.Item>
<Link to="/home">Home</Link>
</Menu.Item>
</Menu>;
}
}
return (
<div>
{loginHeader}
{generalHeader1}
{generalHeader2}
<LoginContext.Consumer>
{(context) => (
this.showContext(context.state.role)
)
}
</LoginContext.Consumer>
</div>
);
}
}
setState() causes a call to render(). So if you call setState() in render() you will get infinite recursion. DON'T DO THIS. Instead find the correct way to do what you want within the framework defined by React and the other libraries you use.
A React component waits for any change on its own state and props in a normal conditions and when a changes takes place, it calls Render method and expect a component or null to be returned.
What you did is: Component starts and tries to do its very first render and you set component states which request another render and it set states again and it goes on like this. It calls each other circularly.
Set state outside of Render method to avoid this situation.
You are doing it in wrong way, as you are setting the state inside the render the render loop will be infinite.
Please check this link which will help you to resolve your problem, and still if you face any issue after implementation let me know I will love to help you.
Reference link : https://reactjs.org/docs/context.html#dynamic-context
Use React lifecycle method componentDidMount to handle such tasks related to setState.
In you are code you have written code in your render which is not good practices. Please remove code from render and add it into componentDidMount. Use componentDidMount() lifecycle which will make your life easy the code should be something like below.
componentDidMount() {
this.showContext(this.state.roleValue)
}
As I have seen you have different side bar for each user. The best thing you can do in this create separate component for each role for sidebar and show that particular component as role wise.

Categories