Strange behavior when generating an id for a React component - javascript

I am trying to generate a unique id number for each React component, this id will be assigned to the component when it is mounted.
I am using the code below, the function callOnce takes a function and ensure that the function is only executed once per component.
import React, { useRef } from "react";
// Helper function that makes fn execute only once
const callOnce = (fn) => {
let called = false;
let result;
return (...params) => {
if (called) return result;
called = true;
result = fn(...params);
return result;
};
};
let lastId = 0;
const getId = () => {
console.log("incrementing");
lastId = lastId + 1;
return lastId;
};
export default function App() {
const { current: getIdOnce } = useRef(callOnce(getId));
const id = getIdOnce();
return <div>{id}</div>;
}
When I execute this code, the id that is displayed is 2, but logically it should be 1.
The getId function is only called once (the message incrementing is printed once), so there is no other way that the variable lastId is incremented again.
Why is the value 2 ?
codesandbox link

I not know what the problem, but the fix can be that:
import React, { useRef } from "react";
// Helper function that makes fn execute only once
const callOnce = (fn) => {
let called = false;
let result;
return (...params) => {
if (called) return result;
called = true;
result = fn(...params);
return result;
};
};
let lastId = 0;
const getId = () => lastId++;
export default function App() {
const { current: getIdOnce } = useRef(callOnce(getId));
const id = getIdOnce();
return <div>{id}</div>;
}
If I found the problem reason, I will update my comment ☺

I found the answer in another thread. Basically when using React with React.StrictMode wrapper, some functions are executed twice.
The reason the id is 2 is because the function getId is executed twice
Link of the answer with more details

Related

Can svelte use composable functions?

I am coming from vue and used to composable functions. I am trying to figure out the way to do this in svelte
So I make a js file and import store and then was trying to make a function that I could call on multiple components and act individually
swipe.js file
import { writable, derived, get } from 'svelte/store';
function createSwipe() {
const dyFromStart = writable(0)
function moveEvent(eventType, val){
console.log('moveEvent', eventType, val, get(dyFromStart))
dyFromStart.update(n => n + 1);
}
const dxScore = derived(dyFromStart, $dyFromStart => $dyFromStart + 3)
const dyScore = derived(dyFromStart, $dyFromStart => Math.round($dyFromStart + 100));
return {
moveEvent,
dxScore,
dyScore,
};
}
export const swipe = createSwipe();
then in .svelte component import function in script and decompose into subparts
<script>
import { swipe } from "$lib/swipe";
let { moveEvent, dxScore, dyScore } = swipe
</script>
<p>{$dxScore}{$dyScore}</p>
<button on:click="() => moveEvent">button</button>
Well eventually I want to turn into a swipe component hence name but trying to get fundamentals down. So I want to be able to have unique store for each component and for this if I use multiple of this .svelte component the state is shared amongst all.
And not just like three idk modal.svelte components I want to use swipe for a bunch of diff components maybe a photoViewer.svelte right just generic swipe function and use same code for all.
or would I just have to keep the state like const dyFromStart = writable(0) be just let dyFromStart = 0 in each .svelte component and pass it into a pure js function that returns results and update local .svelte variables
Adding this as the non store more pure js things I was trying but couldn't get to be reactive so accepting the answer below on store method that worked and sounds like is the correct approach
export function createSwipe() {
let dyFromStart = 0
function moveEvent(eventType, val){
console.log('moveEvent', eventType, val, dyFromStart, dxScore(), dyScore())
dyFromStart++
}
function dxScore(){ return dyFromStart + 3 }
// const dzScore = derived(dyFromStart, $dyFromStart => $dyFromStart + 3)
const dyScore = () => Math.round(dyFromStart + 100)
return {
moveEvent,
dxScore,
dyScore,
dyFromStart
};
export function createSwipe() {
let dyFromStart = 0
let dxScore = dyFromStart + 3
let dyScore = Math.round(dyFromStart + 100)
function moveEvent(eventType, val){
console.log('moveEvent', eventType, val, dyFromStart, dxScore, dyScore)
dyFromStart++
dxScore = dyFromStart + 3
dyScore = Math.round(dyFromStart + 100)
}
return {
moveEvent,
dxScore,
dyScore,
dyFromStart
};
I suppose that works fine just not reactive with $ and need to call to update a diff local var if doing that
this would seem most sveltey to me or something like it as far as composable function type style not store type
export function createSwipe() {
let dyFromStart = 0
function moveEvent(eventType, val){
console.log('moveEvent', eventType, val)
dyFromStart++
}
$: dxScore = dyFromStart + 3
$: dyScore = Math.round($dyFromStart + 100)
return {
moveEvent,
dxScore,
dyScore,
};
}
I don't understand the question fully, so I try to reiterate first what I think you want:
You want to use your swipe function in multiple places
Each usage of that swipe function should be independent of all others
If that's correct, then the answer is simple: Don't do export const swipe = createSwipe(). Delete that part and instead export the create function to use directly within your components. That way you create a new independent instance each time:
<script>
import { createSwipe } from "$lib/swipe";
let { moveEvent, dxScore, dyScore } = createSwipe()
</script>
<p>{$dxScore}{$dyScore}</p>
<button on:click="() => moveEvent">button</button>

How to update value returned by function React (useState implementation)

Let's say I have such a thing, function returning value and setter Function, how Can I implement the setter function correctly to update returned value , every time it is called? (like useState's returned value and the updater function)
const myFunction = (initialValue) => {
let value = initialValue;
const setterFunction = (newValue) =>{
value= newValue;
}
forceRerender() //function that forces re-renders
return [value,setterFunction];
}
const [myValue,updaterFunc] = myFunction('someValue');
updaterFunc('newValue'); // myValue's new Value should be 'newValue'
If you're trying to re-implement how React does it, you would have to have setter functions result in the whole block running again - something like the following:
const state = [];
const App = () => {
let stateIndex = 0; // This variable keeps track of the sequential calls to `myFunction`
// (Needed if myFunction gets called multiple times)
const myFunction = (initialValue) => {
// Assign initial value if it doesn't exist yet
if (!state.hasOwnProperty(stateIndex)) state[stateIndex] = initialValue;
const value = state[stateIndex];
// Need to create a closure over the current state index...
const closureStateIndex = stateIndex;
const setterFunction = (newValue) => {
state[closureStateIndex] = newValue;
// Re-run the entire function asynchronously:
setTimeout(App);
};
// Increment state index to allow for additional calls to myFunction
stateIndex++;
return [value, setterFunction];
}
const [myValue, updaterFunc] = myFunction('initialValue');
// Call updater only on initial render:
if (myValue === 'initialValue') {
updaterFunc('newValue'); // myValue's new Value should be 'newValue'
}
console.log('Rendering. Current value is:', myValue);
};
App();
That's a bit similar to how React does it.
For an example with multiple state variables, and renaming myFunction to useState:
const state = [];
const App = () => {
let stateIndex = 0;
const useState = (initialValue) => {
if (!state.hasOwnProperty(stateIndex)) state[stateIndex] = initialValue;
const value = state[stateIndex];
const closureStateIndex = stateIndex;
const setterFunction = (newValue) => {
state[closureStateIndex] = newValue;
setTimeout(App);
};
stateIndex++;
return [value, setterFunction];
}
const [name, setName] = useState('bob');
const [age, setAge] = useState(5);
if (age === 5) {
setAge(10);
}
console.log('Rendering. Current name and age is:', name, age);
};
App();

will export function() be called multiple times in javascript?

I wonder if this
const getData = () => {
const data = 1; // simplified complicated operations
return data;
};
export default getData();
is any performance difference than this:
const getData = () => {
const data = 1;
return data;
};
const toexport = getData(); // cache to a variable before exporting
export default toexport;
The question boils down to how export actually works. I read many articles and I can manage to make it work, but I haven't understood what it does under the hood (couldn't find an article about my question).
What if an export is imported from difference import, will the getData() be called once or being called for each import?
function getData() {
console.log('getData');
return 1;
}
// will `export default getData();` be like this
const importSomeWhere = getData();
const importSomeWhereElse = getData();
// or this?
const exportCached = getData();
const importSomeWhere2 = exportCached;
const importSomeWhereElse2 = exportCached;
It will be evaluated only once. Here's example from What Happens When a Module Is Imported Twice?
In increment.js, we declare a variable called counter
let counter = 0;
counter++;
export default counter;
In consumer.js we import 2 times, but the counter is evaluated once for the first import
import counter1 from './increment';
import counter2 from './increment';
counter1; // => 1
counter2; // => 1

Implement unregister after registration of callback

I wrote a simple piece of software that allows users to "register" a function when a state is set.
This was easily achieved by adding functions to an array.
I want to return a function that is able to "unregister" that particular function.
Note that a user might register the same function twice. This means that the "unregistering" function cannot be based on the function as a key in a map
The only thing that springs to mind is making the "register" function way more complex, where each item in the "callbacks" array is not just a function, but an object like this:
{
id: someId
fn: [the function]
}
And that the unregister function will filter the someId value. But I just can't like this.
Ideas?
const state = {}
const callbacks = []
const register = (fn) => {
callbacks.push(fn)
return () => {
console.log('Unregister function. HELP!!! How do I do this?')
}
}
const setState = async (key, value) => {
state[key] = value
for (const fn of callbacks) fn(key, value)
}
const getState = (key) => {
return state[key]
}
const f1 = () => {
console.log('f1')
}
const f2 = () => {
console.log('f2')
}
const unregF1a = register(f1)
const unrefF1b = register(f1)
const unregF2 = register(f2)
setState('some', 'a')
unregF1a()
setState('some', 'b')
Loop through your callbacks and remove the desired function (works if the same function is registered twice).
You could do a simple for loop:
function unregister(fn) {
for (let i = callbacks.length - 1; i >= 0; i--) {
if (callbacks[i] === fn) {
callbacks.splice(i, 1)
}
}
}
Or you can use let and replace the whole array:
let callbacks = [];
function unregister(fn) {
callbacks = callbacks.filter(cb => cb !== fn)
}
If you want to be able to register the same function more than once and be able to unregister them independently, then yes, you'll need to track some kind of id.
An id can be something simple, like an increasing integer, and you can store them in a different array, in the same index the function is in the callbacks array (that's hashing).
Something like this:
const state = {}
const callbacks = []
const ids = []
let nextId = 0
const register = (fn) => {
const id = nextId
callbacks.push(fn)
ids.push(nextId)
nextId++
return () => {
// find the function position using the ids array:
const fnIndex = ids.findIndex(cbId => cbId === id)
if (fnIndex === -1) return // or throw something
// Now remove the element from both arrays:
callbacks.splice(fnIndex, 1)
ids.splice(fnIndex, 1)
}
}
This way, the unregister function always looks for the exact index where the id/fn resides.

How to get arguments

The objective of this code is to write the rev function and make it return the following Obviously its
Maybe that's what you wanted. Since you are passing a function as a parameter you are using high order function or a decorator , hope this helps
check this here
function welcome(name) {
return `Welcome ${name}`;
}
function bye(name) {
return `Bye ${name}`;
}
function rev(wrapped) {
return function() {
const result = wrapped.apply(this,arguments);
return `${result}, ${result.split(" ").reverse().join(" ")}`
}
}
const revWelcome = rev(welcome);
const revBye = rev(bye);
console.log(revWelcome('James'))
console.log(revBye('Bond'))

Categories

Resources