Removing component with settimeout - React Class Component - javascript

I have a notification component, which when rendered, I remove it with some delay.
The problem is that when I try to use clearTimeout to cancel the removal, it doesn't. Look
class Notify extends React.Component {
constructor(props){
super(props)
this.class = 'check'
this.state = {load:LineNotify[this.class].anime}
this._delay()
this.timerS = null
this.box = BoxNotify[this.class]
this.icon = IconNotify[this.class]
}
remove=()=>{
// console.log('removed')
unmountComponentAtNode(document.getElementById('notify'))
}
_delay=()=>{
this.timerS = setTimeout(this.remove,2400)
}
stop=()=>{
clearTimeout(this.timerS)
this.setState({load:LineNotify[this.class].line})
}
start=()=>{
this.timerS = setTimeout(this.remove,2400)
this.setState({load:LineNotify[this.class].anime})
}
I have functions to control component removal, they must be called with cursor input, ie if user hovers, I should use clearTimeout to cancel removal ("start,stop")
remove=()=>{
console.log('removed')
// unmountComponentAtNode(document.getElementById('notify'))
}
When I don't use unmountComponentAtNode, using clearTimeout works as expected, but when using unmountComponentAtNode, clearTimeout is ignored and the component is removed anyway

I found a solution. Maybe somehow the timer ID returned by setTimeout is getting lost.
class Notify extends React.Component {
constructor(props){
super(props)
this.class = 'check'
this.state = {load:LineNotify[this.class].anime}
this.timerS = this.timer()
this.box = BoxNotify[this.class]
this.icon = IconNotify[this.class]
}
timer=()=>setTimeout(()=>{
document.getElementById('notify').remove()
},2400)
stop=()=>{
clearTimeout(this.timerS)
this.setState({load:LineNotify[this.class].line})
}
start=()=>{
this.timerS = this.timer()
this.setState({load:LineNotify[this.class].anime})
}

Related

Can't access to a 'this' value in a function outside of constructor

I'm trying to learn how to make an app like reactjs but not really using it. I'm following a tutorial but I have some challenges. I have a function called 'update' which fires when there is a change in the state. I have a 'menu' object that I import as a child. The thing is, I can't seem to access to this child object in the 'update' function. Please have a look at the following:
import onChange from 'on-change';
import Menu from './menu';
class App {
constructor(){
const state = {
showMenu: false
}
this.state = onChange(state, this.update);
this.el = document.createElement('div');
this.el.className = 'todo';
// create an instance of the Menu
this.menu = new Menu(this.state);
// create a button to show or hide the menu
this.toggle = document.createElement('button');
this.toggle.innerText = 'show or hide the menu';
this.el.appendChild(this.menu.el);
this.el.appendChild(this.toggle);
// change the showMenu property of our state object when clicked
this.toggle.addEventListener('click', () => { this.state.showMenu = !this.state.showMenu; })
}
update(path, current, previous) {
if(path === 'showMenu') {
> // show or hide menu depending on state
> console.log (app.menu); // undefined
> this.menu[current ? 'show' : 'hide'](); // not working cause 'this.menu' is undefined
}
}
}
const app = new App();
> console.log (app.menu); // here it console logs correctly the object
document.body.appendChild(app.el);
Can someone help me to figure out what is going on here? thank you!
In the update() method in your App class, you have to use this.menu instead of app.menu.
It should look like this:
update(path, current, previous) {
if(path === 'showMenu') {
console.log (this.menu);
this.menu[current ? 'show' : 'hide']();
}
}
You can't use app within the App class because app is not defined there. You have to use the this keyword to access the members in the class itself.
Hope this helps.

Addeventlistener not recognized on html element in own ES6 class

I am trying to create my own DragDrop class in ES6.
But when I try to set event listeners Chrome dev tools show no attached handlers (onmousex: null).
The browser shows no error and console.log writes that the method "makeDraggable()" has been executed.
Is it possible that the class is called to late in die loading process of the page?
The class DragDrop is called after window.load.
window.onload = function(){
var app = new App();
}
Class App:
class App {
constructor(){
this.dragDrop = new DragDrop(document.querySelectorAll("rect"));
console.log(this.dragDrop);
}
}
Class DragDrop:
class DragDrop {
constructor(elements){
this.elements = elements;
this.makeDraggable();
this.currentElement = null;
}
makeDraggable(){
this.elements.forEach(element => {
element.addEventListener('mousedown', this.start.bind(this)),
element.addEventListener('mousemove', this.drag.bind(this)),
element.addEventListener('mouseup', this.drop.bind(this)),
element.addEventListener('mouseleave', this.drop.bind(this))
});
}
start(evt){
if(this.elements.includes(evt.target)) this.currentElement = evt.target;
}
drag(evt){
if(this.currentElement){
evt.preventDefault();
console.log(this.currentElement.getAttributeNS(null, "x"));
}
}
drop(evt){}
}

How do I create a custom element using a JS class to create the properties for the element

So I know how to create a custom element and have 2 already made that I'm happy with.
My two elems look something like this:
let menuPicker = Object.create(HTMLElement.prototype);
menuPicker.initialized = false;
menuPicker._init = function(){
...
}
...
menuPicker.attachedCallback = menuPicker._init;
menuPicker.createdCallback = menuPicker._init;
...
document.registerElement('menu-picker', {
prototype: menuPicker
});
Which works and all, but this time around I want a JS class. Specifically this feature:
class test {
get item(){...}
set item(){...}
}
So I figured I could just do the following to easily implement this.
class listBox extends HTMLElement.prototype {
initialized = false;
constructor(props){
super(props);
this.attachedCallback = this._init;
this.createdCallback = this._init;
}
_init () {
if(this.initialized) return;
this.initialized = true;
this.style.display = "block";
this.appendChild(document.createElement('div'));
}
}
document.registerElement('list-box', {
prototype: listBox
});
However I get the error Class extends value #<HTMLElement> is not a constructor or null.
So now I'm stuck and can't find a method of using a class to construct a custom HTML element.
To simplify:
How do I create a custom element using a JS class to create the properties for the element?
Here is an example to create a custom element
//ListBox.js
export default class ListBox extends HTMLElement {
// observe attributes of custom element
static get observedAttributes() {
return ["disp", "text"];
}
get myAction() {
return this.hasAttribute("open");
}
set myAction(val) {
if (val) {
this.setAttribute("open", "");
} else {
this.removeAttribute("open");
}
}
constructor() {
super();
this.initialized = false;
this.div = document.createElement("div");
// get and assigns value from props "disp"
this.div.style.display = this.getAttribute("disp");
// get and assigns value from props "text"
this.div.innerHTML = this.getAttribute("text");
this.appendChild(this.div);
}
connectedCallback() {
// didMount
// your methode here
this.initialized = true;
console.log("custom element added to page");
}
disconnectedCallback() {
// unmount
console.log("custom element remove from page");
}
attributeChangedCallback(name, oldValue, newValue) {
// observed props
console.log("props", arguments);
// get and assigns value from props "text"
if (name === "text" && oldValue !== newValue) {
this.div.innerHTML = newValue;
}
}
}
in your html in the script tag which calls your index.js add type="module"
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="app">
<list-box text="Some text here" disp="block"></list-box>
</div>
<script src="src/index.js" type="module"></script>
</body>
</html>
In your index.js import your component and do Something
import ListBox from "./component/ListBox";
customElements.define("list-box", ListBox);
// change props "text value"
document
.querySelector("list-box")
.setAttribute("text", `Change the value of props "text"`);

React component always gives last given props value

Child Component
export class MultiSelectDrawer extends React.Component {
componentDidMount(){
window.onpopstate = ()=>{
console.log(this.props.id);
}
}
render() {
const {input, id, label, showActionDrawer, toggleActionDrawer, items, closeActionDrawer} = this.props;
return (
<div>
</div>
);
}
}
MultiSelectDrawer.contextTypes = {
t: PropTypes.func.isRequired
}
export default MultiSelectDrawer;
Parent component
<td style={Object.assign({},styles.filters, styles.leftAlign)}>
<Field id="stateSearchFilter" name="stateSearchFilter" component={MultiSelectDrawer} label={this.context.t("State")} items={states} titleField='value' toggleActionDrawer = {toggleActionDrawerState} showActionDrawer = {this.state.showActionDrawerState} closeActionDrawer = {closeActionDrawerStateFilter} filterBar={filterBar}></Field>
</td>
<td style={Object.assign({},styles.filters, styles.leftAlign)}>
<Field id="prioritySearchFilter" name="prioritySearchFilter" component={MultiSelectDrawer} label={this.context.t("Priority")} items={priorities} titleField='label' toggleActionDrawer = {toggleActionDrawerPriority} showActionDrawer = {this.state.showActionDrawerPriority} closeActionDrawer = {closeActionDrawerPriorityFilter} filterBar={filterBar}></Field>
</td>
In the child componentDidMount window.onpopstate log I am expecting to see both the ids printed when browser back button is pressed. But it is only printing the second id which is prioritySearchFilter. I am quite new to React. What is wrong i am doing here?
The problem is that you are overwriting your first function.
You are calling:
window.onpopstate = ()=>{
console.log(this.props.id);
}
window.onpopstate = ()=>{
console.log(this.props.id);
}
twice (in each component instance). So its like when you assign two values to the same variable (here the variable is window.onpopstate). The last assignment overwrites the first.
var x = 1;
x = 2 // now x is 2. previous value is overwritten.
Or in your case:
window.onpopstate = func1;
window.onpopstate = func2;
After these two calls window.onpopstate now points to func2

How to reveal a React component on scroll

I've created a React component for a fixed nav that I would like to remain hidden, until I scroll past a certain point on the page, then slides into view. Medium has a header similar to what I'm describing.
This is a relatively trivial task in jQuery, with scrollmagic or waypoints but is there an idiomatic way of accomplishing this with React and vanilla JS?
React Way with vanilla JS jsfiddle;
don't forget to remove EventListener. In this example component will render if only it is neccessary
class TopBar extends React.Component {
state = { isHide: false };
hideBar = () => {
const { isHide } = this.state
window.scrollY > this.prev ?
!isHide && this.setState({ isHide: true })
:
isHide && this.setState({ isHide: false });
this.prev = window.scrollY;
}
componentDidMount(){
window.addEventListener('scroll', this.hideBar);
}
componentWillUnmount(){
window.removeEventListener('scroll', this.hideBar);
}
render(){
const classHide = this.state.isHide ? 'hide' : '';
return <div className={`topbar ${classHide}`}>topbar</div>;
}
}
You could use a component such as react-headroom to do the heavy lifting for you. Or, you can still use waypoints in React, setting it up in the componentDidMount lifecycle method and removing it using componentWillUnmount.
In the componentDidMount lifecycle hook, do the same thing as in the jQuery link you have given:
class Navbar extends React.component {
let delta = 5;
render() {
return (
<div ref=header></div>
);
}
componentDidMount() {
$(window).scroll(function(event){
var st = $(this).scrollTop();
if(Math.abs(this.state.lastScrollTop - st) <= delta)
return;
if (st > lastScrollTop){
// downscroll code
// $(this.refs.header).css('visibility','hidden').hover ()
this.setState({
navbarVisible: false
});
} else {
// upscroll code
$(this.refs.header).css('visibility','visible');
this.setState({
navbarVisible: true
});
}
lastScrollTop = st;
}.bind(this));
}
}
I created a react component for this same exact need as I could not find any other implementations that matched what I needed. Even react-headroom did not give you something that would just scroll in after reaching a certain point on the page.
The gist is here: https://gist.github.com/brthornbury/27531e4616b68131e512fc622a61baba
I don't see any reason to copy the component code here. The code is largely based off of the react-headroom code but does less and is therefore simpler.
The component is the first piece of code, you could simply copy/paste then import it. After importing your code with the navbar would look something like this:
class MyScrollInNavBar extends Component {
render() {
return (
<ScrollInNav scrollInHeight={150}>
<MyNavBar />
</ScrollInNav>
);
}
}

Categories

Resources