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;
Related
Currently I'm using typescript along with react 17.0.2. I made a yarn add react-instagram-embed. I have a component called instagram.tsx where I'm using the following code:
import React from "react";
import InstagramEmbed from "react-instagram-embed";
export default function Instagram({ posts }) {
return (
<div >
<InstagramEmbed
url="https://instagr.am/p/Zw9o4/"
maxWidth={600}
hideCaption={false}
containerTagName="div"
protocol=""
injectScript
onLoading={() => {}}
onSuccess={() => {}}
onAfterRender={() => {}}
onFailure={() => {}}
/>
</div>
);
}
But when I go to my webpage I cannot see the instagram post. I went to inspect element to check out the react developer tools and I could see the correct components:
But when I hover over the components I get
I have tried adding height manually by adding style={{height:"400px"}} in the parent component and maxHeight in the instagramEmbed component but it still does not show up
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
I am trying to use componentwillReceiveProps to change the button color.
when I click the news channel, get top news button should change the color.
so I thought in Button.js I will use componentwillReceiveProps
so that after I receive the props I will change the button color.
but inside componentwillReceiveProps of Button.js nothing is printing.
I researched and found the below link, but still not helping me
How do i use componentWillReceiveProps() correctly?
can you tell me how to fix it?
providing my code snippet and sandbox below
https://codesandbox.io/s/boring-wu-btlre
class Button extends Component {
componentWillReceiveProps(nextprops) {
console.log("componentWillReceiveProps nextprops--->", nextprops);
}
render() {
return (
<div>
<button
onClick={() => {
// getPosts(channel);
// getAlert();
}}
className="btn btn-primary btn-lg btn-block"
>
Get top news
</button>
</div>
);
}
}
That's your app.js from SandBOX and its clearly showing you are not passing anything to the button component. That's why its not showing anything.
import React from "react";
import ChannelsField from "./ChannelsField";
import RecentChannelItem from "./RecentChannelValues";
import Button from "../containers/Button";
import TopNews from "../containers/TopNews";
const App = () => (
<div>
<RecentChannelItem />
<ChannelsField />
<Button />
<TopNews />
</div>
);
export default App;
my home component has:
const Home = () => (
<div>
<Head title="Home" />
<Nav />
<div className="container o_block u_blue">
<div className="notification">
This container is <strong>centered</strong> on desktop.
</div>
</div>
</div>
)
export default Home
and I'm trying to add some DOM manipulation into that component:
componentDidMount() {
let burger = document.querySelector('.burger');
let nav = document.querySelector('#'+burger.dataset.target);
burger.addEventListener('click', function(){
burger.classList.toggle('is-active');
nav.classList.toggle('is-active');
});
}
const Home = () => (
<div>
<Head title="Home" />
<Nav />
<div className="container o_block u_blue">
<div className="notification">
This container is <strong>centered</strong> on desktop.
</div>
</div>
</div>
)
export default Home
but unfortunately I am getting a:
SyntaxError: Unexpected token, expected ";" (8:20)
what am I doing wrong and where should I put the method?
Home is a presentational component in your code. Which means a presentational component is like a pure function in Java Script. A Presentational component doesn’t call any React life cycle method and doesn’t modify React state. It only takes props and returns jsx elements. This is also called as stateless component in react.
If you want to play with React life cycle methods then you should go with statefull component.
componentDidMount is one of React life cycle method so it’s not accessible in presentational or functional components in React.
Edit:
If you want to do DOM manipulation before component initial render then do DOM manipulation in componentWillMount() method but please see this method is deprecated in latest React versions.
If you want to do DOM manipulation after first render then do that in componentDidMount() method. This is the one wr you also make axios calls and do setState accordingly. I would recommend you to go with componentDidMount().
import React, { Component} from "react";
export default class Home extends Component {
componentDidMount() {
let burger = document.querySelector('.burger');
let nav = document.querySelector('#'+burger.dataset.target);
burger.addEventListener('click', function(){
burger.classList.toggle('is-active');
nav.classList.toggle('is-active');
});
}
render(){
return(
<div>
<Head title="Home" />
<Nav />
<div className="container o_block u_blue">
<div className="notification">
This container is <strong>centered</strong> on desktop.
</div>
</div>
</div>
)
}
}
Please excuse me if there are any typo error because I am answering in my mobile
You need to transform your component into a class-based component, like this :
export default class Home extends React.Component {
render() {
// Return the JSX code
}
componentDidMount() {
// Your init code
}
}
But I really suggest you to take a look at the official React doc as it's a fairly simple mistake.
I've got a React Component with several nested components. For page navigation I have a few buttons on the top.
The question is, how to scroll the page to the nested react component when a certain button is clicked, is it possible to do so without using jquery libs?
render() {
return (
<div>
<button onClick={(e) => { console.log('Scrolling to Item 1');}}>Button0</button>
<button onClick={(e) => { console.log('Scrolling to Item 2');}}>Button1</button>
<Layout>
<Item>
<Content>
...
</Content>
</Item>
<Item>
<Content>
...
</Content>
</Item>
</Layout>
}
</div>
);
}
Use scrollIntoView to scroll down to the element and React's refs to get the DOM of your component.
Here is a small example that illustrates what you want to do.
var Hello = React.createClass({
componentDidMount() {
alert("I will scroll now");
this.refs.hello.scrollIntoView(); // scroll...
},
render: function() {
return <div ref="hello">Hello {this.props.name}</div>; // reference your component
}
});
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
Have you tried using an anchor?
Put an id attribute ('myTarget') to your target component and replace the button with a link (href: '#mytarget').
This does not work with fixed headers, unfortunately.
Create a reference using useRef
const trackButton = useRef();
Use scrollIntoView method to scroll to the specific Element/ Component
trackButton.current.scrollIntoView({ behavior: "smooth", block: "start", inline: "nearest" })
If you are using typescript.
import * as React from 'react';
class Hello extends React.Component<{}> {
private helloRef = React.createRef<HTMLDivElement>();
public render() {
return (
<div ref={this.helloRef}>Hello</div>
<button onClick={() => {
if (this.helloRef && this.helloRef.current) {
this.helloRef.current.scrollIntoView();
}
}
}>Button1</button>
);
}
}
Using React hooks and no 3rd party packages:
Create a reference using the React hook useRef
import { useRef } from 'react'
const firstItemRef = useRef(null);
Assign the reference to the component you want to scroll to
<Item ref={firstItemRef}>
Use the scrollIntoView method to scroll to the specific Element/Component
<button onClick={() => firstItemRef.current.scrollIntoView()}
Unless you need to implement it yourself, you can use react-scroll
Their demo is pretty much what you need.
you can use react-scroll
npm install react-scroll --save
and for information can go to this page
https://www.npmjs.com/package/react-scroll