Svelte value not reactive - javascript

I've a variable styles in the Svelte store that I would like to update:
export const styles = writable();
Now in my mainframe.svelte file, I've an EventListener that listens to a click and updates the store value as follow:
document.addEventListener("click", (e) => {
styles.update(() => getComputedStyle(e.target));
});
And in my spacing.svelte file, I want to console.log when the value changes, but it's not at all updating and stuck at undefined:
$: console.log(get(styles));
Now if I use setInterval then it is working and updating the values as per clicks, so it's definitely not the code but the problem is with reactivity itself:
setInterval(() => {
console.log(get(styles));
}, 1000);
What am I doing wrong here? Why the value is not changing automatically on clicks but setInterval seems to work?

This is not working because of how reactivity works.
In your code $: console.log(get(styles)); you have the following parts:
$: this marks the line as reactive, it will run again when any variable (or function) used on that line changes.
console.log, this never changes
get, this is a helper function from the stores, it never changes
styles, this is the store it never changes (the value does, but not the store itself)
conclusion: this line is run once and then never again.
the solution is simple, instead of doing get which is used to get the current value of a store once (and usually only used in script files where reactivity doesn't work), you can simply use the value itself:
$: console.log($styles);

Related

useState getting reset to default when used in combination with useRef in strict mode [duplicate]

After some trial I discovered following problem occurs in strict mode.
I would be interested if someone can explain why.
Take this simple example where inside render I am just scheduling a timeout which updates state:
Example:
let firstRender = true; // Normally I would use ref but I was playing with example
export default function App() {
let [data, setData] = React.useState({ name: 'Nick' });
// Schedule a timeout on first render
if (firstRender) {
setTimeout(() => {
console.log('Running');
setData((ps) => ({
...ps,
name: 'Paul',
}));
}, 1000);
}
console.log('Running render');
firstRender = false;
return (
<div>
<h1>{data.name}</h1>
<p>Start editing to see some magic happen :)</p>
</div>
);
}
If you run this example without Strict mode, then you will see "Paul" on screen after one second, as I was expecting.
If you use Strict mode, it will always show "Nick" on screen. Idea why?
Note: it seems using useRef instead of the global variable firstRender fixes this problem also in Strict mode. This seems to happen because ref was set in first render, and its value also got discarded (see also the answer).
This is due to the fact that strict mode intentionally invokes your function component body twice (when in dev mode) to help spot unintended side effects.
On the second invocation, your firstRender variable is false so your setTimeout doesn't run.
Important to note that this second invocation isn't just a re-render like you'd get from a state update. It's a second invocation of the entire component body. State is not preserved. React invokes your component function once, discards the result, and invokes it a second time to get the output.
From the docs:
Because the above methods might be called more than once, it’s important that they do not contain side-effects.
Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:
Function component bodies
Q. it seems using useRef instead of the global variable firstRender
fixes this problem also in Strict mode. Curious why that's the case?
According to React Docs,
Ref is also useful to keep some mutable value around. The value stores in ref, will not change during subsequent re-renders, unless explicitly changed.
Therefore, for values you don't want to change (maybe some expensive calculation) or for other reasons, use useRef.
The “ref” object is a generic container whose current property is
mutable and can hold any value, similar to an instance property on a
class.
This works because useRef() creates a plain JavaScript object. The
only difference between useRef() and creating a {current: ...} object
yourself is that useRef will give you the same ref object on every
render.
Keep in mind that useRef doesn’t notify you when its content changes.
Mutating the .current property doesn’t cause a re-render. If you want
to run some code when React attaches or detaches a ref to a DOM node,
you may want to use a callback ref instead.

Vuex - function in "watch" not firing if "null" is committed in store action

i am having a problem trying to update some template properties if a Vuex store value changes
when i'm setting the value to undefined inside my store action (for example commit('SET_SELECTED_TICKET_ATTACHMENTS', undefined);), everything seems to work fine.
When setting the value to null however (commit('SET_SELECTED_TICKET_ATTACHMENTS', null);, my watch function will not fire.
The watch function in question looks like this:
selectedTicketAttachments () {
this.isTicketAttachmentLoading = false;
}
The mutation looks like this
SET_SELECTED_TICKET_ATTACHMENTS(state, selectedTicketAttachments){
state.selectedTicketAttachments = selectedTicketAttachments;
},
Any help would me much appreciated!
as EstusFlask has already mentioned, the commit will not be executed if the state will not be changed. My problem was that, under certain conditions, null will already have been commited at a time at which the execution of my watch handler would have gone unnoticed.

If a function updates a state variable which is an object and is called from a child component, what's the best way to avoid infinite renders?

I was doing some coding on React, and encountered an issue I would like to properly deal with. Details on the matter are provided below.
The Environment
Suppose you have a component FormDemo made to handle a potentially complex form, parts of which involve the dynamic management of certain input fields. As an example, the provided code sample allows to create any amount of fields for names between 0 and (232 - 1) fields due to JavaScript's limitations on array length.
Press Add New Name button above all name fields to append another name field. Press Remove button to the right of any input to delete it from the list.
Each name input created is handled by a separate component SubForm that takes three properties:
id: a unique generated identifier of the current field.
onChange: a function executing whenever the value of that input was changed.
onRemove: a function executing whenever the Remove button of that form was clicked.
The Sample
Here is a working sample of a code I've made on CodeSandbox provided for demonstration purposes.
The Problem
The approach used in the code sample works, but it has the eslint problem mentioned in Problems tab of CodeSandbox, and I am aware that it's not a CodeSandbox issue, as I've tested the same project in my local environment and got the same problem. Here are that problem's details taken right from the console:
React Hook useEffect has a missing dependency: 'onChange'. Either include it or remove the dependency array. If 'onChange' changes too often, find the parent component that defines it and wrap that definition in useCallback. (react-hooks/exhaustive-deps)
Following the advice from the problem directly (i.e. adding onChange to dependency list of SubForm's useEffect) results in infinite rendering, and thus is not a solution to the problem.
The Research
After some reading of the official React docs on useCallback, as well as the other part of these on useEffect, I've figured out that, when rendering a component, React creates new instances of functions declared in a component's body. Therefore, adding such functions to a dependency list of some useEffect hook that has an effect function attached to it will entail that function being called on each render.
In my approach, I pass update function to SubForm component in onChange property as a reference (proven here by React docs), hence the SubForm component's onChange property has exactly the same instance of the update function as the parent component. So, whenever the instance of the update function changes with it added to the dependencies of a useEffect hook, that executes the effect function attached to it, and, taking the above into account, this happens on each render of a parent component FormDemo.
The update function changes the value of forms, a state variable of FormDemo component, causing it to rerender. That recreates the instance of an update function. The SubForm component gets notified of that change and executes an effect function attached to a useEffect hook, calling the update function once again. In turn, this causes another change of a state variable forms, telling the parent component FormDemo to render again... and this continues indefinitely, creating an infinite loop of renders.
Some of you may ask why does it happen if an input field of the form was not changed between these two renders, thus the value passed to update function is effectively the same as before. After some testing, which you can try yourself here, I came to the conclusion that it's actually wrong: the value set to forms is always different. That's because even though the object's content is exactly the same, its instance is different, and React compares object instances instead of their contents, sending a command to rerender the component if these instances differ.
As useCallback hook memoizes the instance of the function between renders, recreating the function only when the values or instances of its dependencies change, I've assumed that wrapping update function in that hook will solve the original problem, because the instance of the function will always stay the same.
However, wrapping the update function in useCallback will result in another problem: I will need to add forms as a dependency, because I'm using it inside that function. But, taking the above into account, this will bring me the original problem back due to the instance of forms being different after each update, and that will command useCallback to recreate the instance of the function, too.
Potential Solution
With all that being said, I have a solution that I don't quite like, even though it works because it removes the need of adding the state variable forms to the list of dependencies of useCallback:
const update = useCallback((id, value) => {
setForms(prevState => {
const { form_list } = prevState,
new_forms = [...form_list],
mod_id = new_forms.map((e) => e.id).indexOf(id);
new_forms[mod_id] = value;
return { ...prevState, form_list: new_forms };
});
}, []);
So why am I against it, if it works and gives no problems in the console?
In my humble opinion (feel free to prove me wrong), because of these issues:
Direct usage of state setter function instead of a dedicated middleware function. This decentralizes direct state management.
Duplication of an original array, which may be expensive on memory if an array has a lot of values inside, not to mention that each value itself is an object.
The Question
What is the most memory-efficient and readable solution of the stated problem in the provided case that will use a middleware function setField? Alternatively, if it's possible to debunk my issues with a potential solution, prove that it's the best way to go.
Feel free to modify the contents of setField if necessary for the solution and remember that I'm all open for answering anything related to the question.
It seems you are duplicating state of each SubForm: you store it in parent and also in SubForm, why not store state only in parent and pass as props?
I am talking about something like this:
const SubForm = ({ id, form, onChange, onRemove }) => {
return (
<Form>
<Form.Group controlId={`form_text${id}`}>
<Form.Label>Name (ID {id})</Form.Label>
<InputGroup>
<Form.Control
type="text"
value={form.name}
onChange={(e) => onChange(id, { ...form, name: e.target.value })}
/>
<Button variant="danger" onClick={() => onRemove(id)}>
Remove
</Button>
</InputGroup>
</Form.Group>
<br />
<br />
</Form>
);
};
To pass each form data just do:
<SubForm key={e.id} form={e} id={e.id} onChange={update} onRemove={remove} />
No need for useEffect anymore.
You probably want to separate the management of IDs from SubFrom. SubForm shouldn't be able to change it's ID.
Wrap the update & remove functions - so SubForm doesn't need to send the ID back.
<SubForm key={e.id} id={e.id}
onChange={(form) => update(e.id, form)}
onRemove={() => remove(e.id) } />
Make sure that SubForm will not change ID as part of form
const update = (id, value) => {
setField(
"form_list",
// subform shouldn't change id, so we overriding it (to be sure)
forms.form_list.map(e => e.id===id?{...value, id}:e)
);
};
You still optionally may pass the ID to the SubForm, but the management of IDs is separated from it.
Modified code

Is there a simple way in Javascript to run a function when a specific object property changes?

I am using a third party library that gives me a reference to its object when it initialises:
const flktyInit = (ref) => {
flkty = ref.flkty;
console.log(ref.state.flickityReady) //returns false on page load, but few seconds later returns true
};
What I am trying to make sure of is before I run a method from flkty, ref.state.flickityReady is true first. I have achieved this in a hacky form with setInterval, but this feels too hacky and can't find an obvious answer.
FYI this app is in a React environment.
Can anyone offer up a suggestion?
In case you want to use vanilla javascript you'll have to use setInterval.
Since you've mentioned that you are using ReactJS, and as long as this behavior related to a component, I would recommend handling it as the component state, so when the state changes (and turning from false to true), you can execute a function call using useEffect (inside that useEffect you will have to check if the value is true or false before calling the function you want, But it will trigger useEffect each time the state of the component was updated.

Firebase (Google) Cloud Functions - Debounce/Throttle database.onWrite() #AskFirebase

Scenario
I have documents stored for each user at path documents/${documentId}
Goal
I want to parse them and update the index for that document when it changes
Code
import Functions from 'firebase-functions'
export writeTrigger = Functions
.database
.ref('/document/{documentId}')
.onWrite(
async event => {
const data = event.data.val()
const { documentId } = event.params
// assume that updateIndex function exists
updateIndex(documentId, data)
}
)
Problem
This function gets called for every single letter being typed into the document
TLDR
What is the best way to throttle/debounce firebase cloud functions (database.onWrite) so that it isn't fired on each and every change?
Your function will get invoked for each and every change at or under the path you specify. There's currently no way to prevent this.
Instead of writing each and every change to the database, instead try batching up changes on the client and writing them out in bulk, or saving state periodically.
Alternatively, give the client some other way to indicate that it's time for the function to do work, maybe some field in the document and listen to only that field's changes. Here's one that just triggers when a field done is changed:
export writeTrigger = Functions
.database
.ref('/document/{documentId}/done')
.onWrite(...)
Just be sure to unset that value so that the client can indicate another set of changes should be processed.

Categories