I have a hook that adds a header tag in order to load a script, but in case two components are using it, the hook could fall into the case that the header is added twice.
There are several ways to avoid this. Two of them could be:
Use a provider, not a hook that several components could call. Actually I don't want this, I want a hook because at most two components will use it, and in some cases I don't even need it;
The hook calls a function called const addHeaderTag = () => {...}. I could add a property to it and check if the function is called just once, otherwise return silently. It could be safe because the function is defined by me and I control the function object, plus javascript is monothread and concurrency is out of scope;
I could add an id to the header so as to check if it's on the DOM already. I'd avoid it in order to not access the DOM too much
Do you see other better ways? Do you see any problem to the solutions I had in mind?
A solution for this would be using a variable outside of your custom hook to check whether or not your hook is already called
import { useEffect } from "react";
let isCalled = false;
export const useOnce = () => {
useEffect(() => {
if (!isCalled) {
// do this only once, call your function here
console.log("hey there");
isCalled = true;
}
}, []);
return isCalled;
};
The reason this works is because when you import the same module multiple times, the code in that module is still only evaluated once.
That means isCalled in this case is only initialized once, so we can depend on it to check/set the value accordingly for the entire app.
Live example
Related
I need to create an event handler in React in a custom hook and I'd like the event handling function to be referentially equal across renders for ..reasons (yes I do see how I can do this w/o but I want to understand). Can I use useCallback as below to ensure the eventListener is referentially equal on every render by simply not mentioning variables it closes over in the dependencies (as below) or will that cause issues even if it's never passed to a child component so I don't need it to change value to trigger rerenders?
If not how can I achieve this? Does it matter if I can assume delay and fn don't change (except perhaps it's reference)?
export const useVisibleInterval = (fn, delay = 300000) => {
const interval_id = useRef(null)
const handleVisibilityChange = useCallback(() => {
if (document.hidden || interval_id.current) return
interval_id.current = setInterval(fn, delay)
fn()
}, [])
const clearInt = () => {
if (!interval_id.current) return
clearInterval(interval_id.current)
interval_id.current = null
}
const interval_run = () => {
if (document.hidden) {
document.addEventListener("visibilitychange", handleVisibilityChange)
clearInt()
} else fn()
}
const teardown = () => {
document.removeEventListener("visibilitychange", handleVisibilityChange)
clearInt()
}
useEffect(() => {
interval_id.current = setInterval(interval_run, delay)
return teardown
}, [])
}
At a really high level, I'd like to understand what the dependencies for useCallback do. Are they just to force reference inequality so child components that depend on it rerender? Or does React memoize the result of function execution so failing to include dependencies would give stale results?
Based on pilchard's comment I've figured out the answer (and that I was being an idiot).
The issue is merely about what values are updated when the function gets called with new arguments. References don't need to be listed in the dependencies of a useCallback so the way I can do this is simply to replace fn and delay with references that take on those values, e.g.,
ref_fn = useRef(fn)
useEffect(()=> {ref_fn.current = fn}, [fn]}
And likewise for delay (or stuff them into same ref). But, it turns out that I didn't even need the useCallback and referential equality and the code above reflects my deep confusion at the time and should be ignored.
I am concerning about the way we declare a method in VueJs. Basically I am learning VueJs and while reading this docs, specifically understanding the example. I realized that there is another way we can declare a method in Vue instance. Like the piece of code below:
watch: {
// whenever question changes, this function will run
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
},
created: function () {
// _.debounce is a function provided by lodash to limit how
// often a particularly expensive operation can be run.
// In this case, we want to limit how often we access
// yesno.wtf/api, waiting until the user has completely
// finished typing before making the ajax request. To learn
// more about the _.debounce function (and its cousin
// _.throttle), visit: https://lodash.com/docs#debounce
this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
}
The debouncedGetAnswer method make me confused.
So as what I have seen we can declare method in created lifecycle hook and use it later on(in this case is in watch functionality).
So what are differences between declare methods in methods property and lifecycle hook such as created or mounted, whether former is public(mean we can use in template) and later is private(mean we only use in lifecycle hook) respectively?
Thanks in advance.
I'm using socket.io along with react.js. I need to declare some event handlers for the socket, Also because of some modules I'm using, I can't use classic class components and therefore I'm using functions along with hooks. The problem is initializing the event handlers using the useEffect hook results in redeclaring the socket event handlers every time a new render happens, Thus, each event gets connected with a bunch of event handlers instead of only one, which results in multiple calls of the desired event handler when the event happens. There is way to make the useEffect run only once; Passing an empty array as a second argument to it (which kind of makes it like a constructor in class components). This prevents multiple calls on the event handler but, This also prevents the event handler from accessing the components latest state. Is there a way to achieve both?
var socket = io('http://localhost:10000');
const [var, setvar] = useState(0);
useEffect(() => {
socket.on('event', function(data) { //event handler function
console.log(var);
});
}, []);
For further explanation, The code above calls the event handler function only once per event but it always outputs the initial state of var which is 0. On the other hand, if the [] argument is removed, The handler will output the current value of var but it outputs it multiple times, Because for each render a new event handler is connected to the socket so the number of outputs depends on the number of renders that happened before that event.
Instead of the square brackets, you could pass in a 'dependency' for example [isInit]. The effect will only run if the value of isInit changes. You should declare isInit using useState and default it to false, i.e const [isInit, setIsInit] = useState(false). Then when you run the useEffect the first time call set setIsInit(true). Then in the useEffect check if (!isInit) ... this will control it nicely for you.
You can try combining what you have with a useRef, something like:
var socket = io('http://localhost:10000');
const [var, setvar] = useState(0);
const ref = useRef({current: var});
const alteredSetvar = useCallback((var) => {
setvar(var);
ref.current = var;
}, [setvar, ref]);
useEffect(() => {
socket.on('event', function(data) { //event handler function
console.log(ref.current);
});
}, []);
would work I think (call alteredSetvar instead of setvar.
useRef is typically used to hold a reference to a DOM node, but in reality it just holds an immutable reference, of which you can alter the inner value as much as you needed.
I am trying called the react component's function from a javascript function. On the initial stage, I want to call my function which is declared in react component but rather than a call from react I need to call it from a javascript function.
I know how to call a javascript function from react function. like below
Javascript
<script type="text/javascript">
function test(){
alert('Function from index.html');
}
</script>
React
componentWillMount() {
window.test();
}
But in my case, I want a totally reverse process, need to call react function from a javascript due to certain plugin initialization.
I went through other related topics but don't get an exact answer. so how should I do this?
You can can assign a function to the window object as a function and call that from your script. So essentially you would do the following:
componentWillMount() {
window.reactFunction = () => {
console.log('Test');
}
}
Then call it from your script like so:
<script type="text/javascript">
function test(){
window.reactFunction()
}
</script>
Admittedly I have no idea how exactly you plan to use this but this could be one way of accomplishing what you want by utilizing the global window object. The second part would realistically need some way of determining whether the react component has mounted (in a non trivial example), or just check for the existence of the method before calling it.
I was just writing a piece of code that updates a parent component's state. This piece of code:
handlePress() {
this.props.count();
this.setState({completed: true});
};
managed to update the parent's state, where as this one
handlePress() {
this.props.count;
this.setState({completed: true});
};
did not. Therefore, what is the difference between calling functions with and without parentheses?
All you've done is referenced the function that you've passed in as a prop, you haven't attempted to execute it. What you've done is no different than if you had done this:
let count = 6;
count
That second line would be useless, as is when you simply use this.props.count instead of actually calling the function with this.props.count()