I need to callback. a. function after setting a state within a nested function which triggers on click, How can I approach this, I know that calling hooks in nested functions doesn't work but it's necessary in my case. I tried using useEffect and useCallback but as expected it wont work The code looks like this in a nutshell:
const App = () => {
const [elements, setElements] = useState(true);
function UseEffectSkipFirst(fn, arr) {
const isFirst = useRef(true);
useCallback(() => {
if (isFirst.current) {
isFirst.current = false;
return;
}
fn();
}, arr);
}
const Shape = (g) => {
// let colorMatrix = new PIXI.filters.ColorMatrixFilter();
// let color = 0xffffff
// let tint = 0xffffff;
function onClick(event) {
setElements(false);
UseEffectSkipFirst(
() => {
if (elements === true) {
this.data = event.data;
this.dragging = true;
this.offX = this.x - this.data.getLocalPosition(this.parent).x;
this.offY = this.y - this.data.getLocalPosition(this.parent).y;
} else {
g.clear();
}
},
[elements]
);
}
// ...
};
return (
//...
);
};
can anyone help please, thanks
Related
I really need some help with media queries. I have made a testimonial slider and when it gets to a 800px breakpoint, I want to disable the JS for the slider and change the layout.
The problem is that I am unable to deactivate the js once it has been activated, I'm not sure if the problem is from the event handler, the if statement of from the deactivation function.
I have managed to deactivate the JS but I have to reload the page to do it, I want it to be responsive so that I don't have to reload the page every time.
If you can help, that would be a huge help, thanks!
Here is my JS code:
(All my css code works, hence, it hasn't been added).
const mQ = window.matchMedia('(max-width: 800px)');
const testimonialSlider = function () {
const slides = document.querySelectorAll('.slide');
const btnLeft = document.querySelector('.slider__btn--left');
const btnRight = document.querySelector('.slider__btn--right');
const dotContainer = document.querySelector('.dots');
const testimonialContainer = document.querySelector(
'.testimonial-outer-container'
);
//Deactivate functions
const deactivateJs = function (slide) {
slides.forEach(
(s, i) => (s.style.transform = `translateX(${0 * (i - slide)}%)`)
);
testimonialContainer.removeChild(dotContainer);
console.log('DeactivatingJs');
};
// Activate functions
const activateJs = function () {
console.log('ActivateJS');
let curSlide = 0;
const maxSlide = slides.length;
//Functions
const createDots = function () {
testimonialContainer.appendChild(dotContainer);
slides.forEach(function (_, i) {
dotContainer.insertAdjacentHTML(
'beforeend',
`<button class="dots__dot" data-slide="${i}"></button>`
);
});
};
const slideReady = function () {
createDots();
activateDot(0);
goToSlide(0);
};
const goToSlide = function (slide) {
slides.forEach(
(s, i) => (s.style.transform = `translateX(${100 * (i - slide)}%)`)
);
};
// Activate dots
const activateDot = function (slide) {
// First we remove the active class on all of the dots and then we add it to the one that it is active
document
.querySelectorAll('.dots__dot')
.forEach(dot => dot.classList.remove('dots__dot--active'));
document
.querySelector(`.dots__dot[data-slide="${slide}"]`)
.classList.add('dots__dot--active');
};
// Next slide
const nextSlide = function () {
if (curSlide === maxSlide - 1) {
curSlide = 0;
} else {
curSlide++;
}
goToSlide(curSlide);
activateDot(curSlide);
};
const prevSlide = function () {
if (curSlide === 0) {
curSlide = maxSlide - 1;
} else {
curSlide--;
}
goToSlide(curSlide);
activateDot(curSlide);
};
slideReady();
//Event Handlers
btnRight.addEventListener('click', nextSlide);
btnLeft.addEventListener('click', prevSlide);
// Creating an event with the arrows
document.addEventListener('keydown', function (e) {
console.log(e);
if (e.key === 'ArrowLeft') prevSlide();
if (e.key === 'ArrowRight') nextSlide();
});
dotContainer.addEventListener('click', function (e) {
if (e.target.classList.contains('dots__dot')) {
/// const slide = e.target.dataset.slide;
//This is the same as above, the slide part is from the html and the dataset is an HTML attribute
const { slide } = e.target.dataset;
goToSlide(slide);
activateDot(slide);
}
});
};
// if (mQ.matches) {
// deactivateJs();
// } else {
// activateJs();
// }
const js = function () {
if (mQ.matches) {
console.log('deactivate');
deactivateJs();
} else {
activateJs();
}
};
mQ.addEventListener('change', js);
};
testimonialSlider();
Have to use same function with two different variable with querySelector attached to the variable. Following is the javascript.
var productLink = document.querySelector('.products-link');
var productOffcanvas = document.getElementById('offcanvasProducts')
productOffcanvas.addEventListener('show.bs.offcanvas', function () {
element.classList.toggle('active');
})
productOffcanvas.addEventListener('hide.bs.offcanvas', function () {
productLink.classList.toggle('active');
})
var solutionsLink = document.querySelector('.solutions-link');
var solutionsOffcanvas = document.getElementById('offcanvasSolutions')
solutionsOffcanvas.addEventListener('show.bs.offcanvas', function () {
solutionsLink.classList.toggle('active');
})
solutionsOffcanvas.addEventListener('hide.bs.offcanvas', function () {
solutionsLink.classList.toggle('active');
})
Using a function which returns a function:
function func(link) {
return () => link.classList.toggle('active');
}
Then:
productOffcanvas.addEventListener('show.bs.offcanvas', func(element));
productOffcanvas.addEventListener('hide.bs.offcanvas', func(productLink));
solutionsOffcanvas.addEventListener('show.bs.offcanvas', func(solutionsLink));
solutionsOffcanvas.addEventListener('hide.bs.offcanvas', func(solutionsLink));
You can use same listner for both function and differentatie them with their class names.
const productLink = document.querySelector('.products-link');
const productOffcanvas = document.getElementById('offcanvasProducts');
productOffcanvas.addEventListener('show.bs.offcanvas', showOffCanvas);
productOffcanvas.addEventListener('hide.bs.offcanvas', hideOffCanvas);
const solutionsLink = document.querySelector('.solutions-link');
const solutionsOffcanvas = document.getElementById('offcanvasSolutions')
solutionsOffcanvas.addEventListener('show.bs.offcanvas', showOffCanvas);
solutionsOffcanvas.addEventListener('hide.bs.offcanvas', hideOffCanvas);
function showOffCanvas(e) {
const classList = e.target.classList;
if(classList.contains('products-link')) {
productLink.classList.toggle('active');
} else {
solutionsLink.classList.toggle('active');
}
}
function hideOffCanvas(e) {
const classList = e.target.classList;
if(classList.contains('products-link')) {
productLink.classList.toggle('active');
} else {
solutionsLink.classList.toggle('active');
}
}
You can use like:
function toggleCanvasVisibility(targetElement, el) {
targetElement.addEventListener('show.bs.offcanvas', function () {
el.classList.toggle('active');
})
targetElement.addEventListener('hide.bs.offcanvas', function () {
el.classList.toggle('active');
})
}
function findAndRegisterCanvasVisibility(canvasId, targetClass) {
const element = document.querySelector(targetClass);
const canvasElement = document.getElementById(canvasId);
toggleCanvasVisibility(canvasElement, element);
}
// for product link
findAndRegisterCanvasVisibility('offcanvasProducts', '.products-link');
// for solutions
findAndRegisterCanvasVisibility('offcanvasSolutions', '.solutions-link');
I'm learning throttling and I'm having an issue where my throttle method is not waiting the limit time to run.
const display = (msg) => {
console.log(msg). // I know this function does not do anything, but I'm trying to understand how I can call a function inside my throttle.
}
const throttle = (func, limit) => {
let flag = true;
return function() {
if(flag) {
func.apply(this, arguments);
flag = false;
setTimeout(() => flag = true, limit);
}
}
}
const throttleDisplay = () => {
return throttle(display("Hi"), 6000);
}
for(let i=1; i<=10; i++) {
setTimeout(throttleDisplay, i*1000);
}
My output is "Hi" 10 times, but I shouldn't have 10 times Hi because I have a 6s wait between one call and another.
throttle takes a callback as a parameter, but you're invoking display immediately.
const throttleDisplay = () => {
return throttle(display("Hi"), 6000);
}
is exactly equivalent to
const throttleDisplay = () => {
const result = display("Hi");
return throttle(result, 6000);
}
See the problem?
You need a function that invokes display with the argument you want instead:
const throttleDisplay = () => {
return throttle(() => display("Hi"), 6000);
}
Good morning/afternoon everyone,
I've been using React for a few months. I'm trying to avoid using the React Components, instead I use the React Hooks, but I have to admit that there are times when my goals get complicated.
One of those moments is when executing a function only once after rendering the component. In my case I want to execute a recursive function (typeText) only once after all the components have been rendered.
Here is the link to CodeSandbox of the project (React): https://codesandbox.io/s/execute-after-rendering-jzz87
This is what I would like to achieve (Static): https://codesandbox.io/s/type-effect-jfzhl
Below there is an example of the react project:
import React, { useEffect, useState } from "react";
export default function App() {
const [word, setWord] = useState("");
const list = ["Bag", "Door", "Shelving"];
let isWriting = true;
let selectedWord = 0,
position = 0,
delay = 0;
const typeText = () => {
if (isWriting === true) {
if (position < list[selectedWord].length) {
setWord(word + list[selectedWord].charAt(position++));
delay = 100;
} else {
isWriting = false;
delay = 1500;
}
} else {
if (word.length > 0) {
setWord(word.substring(0, word.length - 1));
delay = 40;
} else {
isWriting = true;
selectedWord = (selectedWord + 1) % list.length;
position = 0;
delay = 300;
}
}
setTimeout(() => typeText(), delay);
};
useEffect(() => {
typeText();
}, []);
return (
<div className="App">
<h1>{word}</h1>
</div>
);
}
Thank you very much to all of you for your help, greetings and a hug!
What you posted isn't React code though, would you mind posting the React part? In any case, in hooks, to run something once after rendering, just use the useEffect hook:
const MyComponent = () => {
useEffect(() => {
// your code here
}, []);
return <Whatever you="are rendering" />;
};
Problem solved, you can see the changes in https://codesandbox.io/s/execute-after-rendering-jzz87.
Thank you all!
I installed Shuffle.js from a codepen demo with some customizations to style and figure cards. I've added in recommended js code but the shuffle function doesn't seem work.
Getting several errors:
console errors
I've tried updating script based on some past answers to this problem. I've also downloaded and added the actual js file on my site and referenced it in the script. Here's what my script looks like right now:
<script src="/v/vspfiles/assets/js/shuffle.js"></script>
<script>
'use strict';
var Shuffle = window.shuffle;
var Demo = function (element) {
this.element = element;
// Log out events.
this.addShuffleEventListeners();
this.shuffle = new Shuffle(element, {
itemSelector: '.picture-item',
sizer: element.querySelector('.my-sizer-element'),
});
this._activeFilters = [];
this.addFilterButtons();
this.addSorting();
this.addSearchFilter();
this.mode = 'exclusive';
};
Demo.prototype.toArray = function (arrayLike) {
return Array.prototype.slice.call(arrayLike);
};
Demo.prototype.toggleMode = function () {
if (this.mode === 'additive') {
this.mode = 'exclusive';
} else {
this.mode = 'additive';
}
};
/**
* Shuffle uses the CustomEvent constructor to dispatch events. You can listen
* for them like you normally would (with jQuery for example). The extra event
* data is in the `detail` property.
*/
Demo.prototype.addShuffleEventListeners = function () {
var handler = function (event) {
console.log('type: %s', event.type, 'detail:', event.detail);
};
this.element.addEventListener(Shuffle.EventType.LAYOUT, handler, false);
this.element.addEventListener(Shuffle.EventType.REMOVED, handler, false);
};
Demo.prototype.addFilterButtons = function () {
var options = document.querySelector('.filter-options');
if (!options) {
return;
}
var filterButtons = this.toArray(
options.children
);
filterButtons.forEach(function (button) {
button.addEventListener('click', this._handleFilterClick.bind(this), false);
}, this);
};
Demo.prototype._handleFilterClick = function (evt) {
var btn = evt.currentTarget;
var isActive = btn.classList.contains('active');
var btnGroup = btn.getAttribute('data-group');
// You don't need _both_ of these modes. This is only for the demo.
// For this custom 'additive' mode in the demo, clicking on filter buttons
// doesn't remove any other filters.
if (this.mode === 'additive') {
// If this button is already active, remove it from the list of filters.
if (isActive) {
this._activeFilters.splice(this._activeFilters.indexOf(btnGroup));
} else {
this._activeFilters.push(btnGroup);
}
btn.classList.toggle('active');
// Filter elements
this.shuffle.filter(this._activeFilters);
// 'exclusive' mode lets only one filter button be active at a time.
} else {
this._removeActiveClassFromChildren(btn.parentNode);
var filterGroup;
if (isActive) {
btn.classList.remove('active');
filterGroup = Shuffle.ALL_ITEMS;
} else {
btn.classList.add('active');
filterGroup = btnGroup;
}
this.shuffle.filter(filterGroup);
}
};
Demo.prototype._removeActiveClassFromChildren = function (parent) {
var children = parent.children;
for (var i = children.length - 1; i >= 0; i--) {
children[i].classList.remove('active');
}
};
Demo.prototype.addSorting = function () {
var menu = document.querySelector('.sort-options');
if (!menu) {
return;
}
menu.addEventListener('change', this._handleSortChange.bind(this));
};
Demo.prototype._handleSortChange = function (evt) {
var value = evt.target.value;
var options = {};
function sortByDate(element) {
return element.getAttribute('data-created');
}
function sortByTitle(element) {
return element.getAttribute('data-title').toLowerCase();
}
if (value === 'date-created') {
options = {
reverse: true,
by: sortByDate,
};
} else if (value === 'title') {
options = {
by: sortByTitle,
};
}
this.shuffle.sort(options);
};
// Advanced filtering
Demo.prototype.addSearchFilter = function () {
var searchInput = document.querySelector('.js-shuffle-search');
if (!searchInput) {
return;
}
searchInput.addEventListener('keyup', this._handleSearchKeyup.bind(this));
};
/**
* Filter the shuffle instance by items with a title that matches the search input.
* #param {Event} evt Event object.
*/
Demo.prototype._handleSearchKeyup = function (evt) {
var searchText = evt.target.value.toLowerCase();
this.shuffle.filter(function (element, shuffle) {
// If there is a current filter applied, ignore elements that don't match it.
if (shuffle.group !== Shuffle.ALL_ITEMS) {
// Get the item's groups.
var groups = JSON.parse(element.getAttribute('data-groups'));
var isElementInCurrentGroup = groups.indexOf(shuffle.group) !== -1;
// Only search elements in the current group
if (!isElementInCurrentGroup) {
return false;
}
}
var titleElement = element.querySelector('.picture-item__title');
var titleText = titleElement.textContent.toLowerCase().trim();
return titleText.indexOf(searchText) !== -1;
});
};
document.addEventListener('DOMContentLoaded', function () {
window.demo = new Demo(document.getElementById('grid'));
});
</script>
Any insight into what I need to remedy to get this working properly would be great. Thanks!