I have the following selector:
const getAllAddresses = (withStartEnd) =>
createSelector(
[getAllAddressesSelector, getStartAddressSelector, getEndAddressSelector],
(all, startAddress, endAddress) => {
if (!withStartEnd) return [...Object.values(all)];
return [startAddress, ...Object.values(all), endAddress];
}
);
I noticed that the selector is re-calculating every time, event when all, startAddress and endAddress do not change. If I remove the input for the selector function, to something like this:
const getAllAddresses = (
createSelector(
[getAllAddressesSelector, getStartAddressSelector, getEndAddressSelector],
(all, startAddress, endAddress) => {
return [startAddress, ...Object.values(all), endAddress];
}
)
);
Then everything works as expected and the selector does not re-calculate on every call. Seems like I missing something in the selector concept. Any help would be much appreciated.
Update:
Please refer to How do I create a selector that takes an argument?
In short: the way you did it, will work only if you pass static arguments and create a factory function outside of mapStateToProps. For dynamic arguments it's more complex and please follow the resource I already mentioned above.
The reason your selector is recalculated each time mapStateToProps is called is that calling getAllAddresses will create a new instance of createSelector and the memoization won't work.
Original answer:
In short, reselect determines input selector changes, based on identity check ===.
Therefore, if your input selectors always create and return a new object or array, then your selector will be recalculated each time.
In order to fix the recalculation issues:
Make sure your input selectors always return a reference, instead of new object / array.
Or, if a new object / array is the proper returned value, then you have to customize the equalityCheck for defaultMemoize
From the reselect docs:
Why is my selector recomputing when the input state stays the same? (please follow the link, there are great examples)
Check that your memoization function is compatible with your state update function (i.e. the reducer if you are using Redux). For example, a selector created with createSelector that recomputes unexpectedly may be receiving a new object on each update whether the values it contains have changed or not. createSelector uses an identity check (===) to detect that an input has changed, so returning a new object on each update means that the selector will recompute on each update.
Related
There is an array of inputs in two forms: 1. Empty when created. 2. With the created values from the server after creation and saving. My code for adding a value for each of the inputs in the array doesn't work in the second case and I don't understand what could be wrong. In the first case, the values are written normally, and in the second, nothing happens.
<input
defaultValue={sectionValue[i].text}
value={sectionValue[i].text}
onChange={(e: React.ChangeEvent<HTMLInputElement>): void => {
sectionValue[i].text = e.target.value;
setSectionValue([...sectionValue]);
}}
/>
There are two issues:
Any time you're changing state based on existing state, you're best off using the callback form of your state setter, because it's really easy for the handler to close over a stale copy of the state (sectionValue in your case).
You're breaking one of the rules of state: don't directly modify state. You're directly modifying the sectionValue[i] object. So it's the same object, but with a different text property. Later you're copying the array, but you need to copy the object as well. Sometimes it'll happen to render correctly when you do this, but other times — often — it won't.
To fix both, change:
sectionValue[i].text = e.target.value;
setSectionValue([...sectionValue]);
to
setSectionValue(oldSectionValue => {
const newSectionValue = [...oldSectionValue];
newSectionValue[i] = {
...oldSectionValue[i],
text: e.target.value
};
return newSectionValue;
});
There's more than one way to do that, but the basic things are: 1. Use the callback, and 2. Copy both the object and the array.
Side note: Since sectionValue is an array, I'd suggest using the plural for it (sectionValues).
I'm using React hooks to implement a form. The state of the form is being managed as an array of objects. A feature/bug of React appears to be that copies of arrays are processed/unified/mutated as a single array. That is, changes to one array become changes in all copies of the array.
I do not want to mutate the array as it creates an issue when attempting to use an array to initialize and reset the state. Once the initialization array becomes unified with the state array, the reset function will no longer work. I have made some attempts at preventing array unification without success. Below is the problem, some of my attempts to resolve the issue and a work around.
Lets say the state of the form is defined as:
const [field, setField] = useState(initialField);
Once this code executes, the value of array initialField is unified with the value of the array field.
Lets say we want to reset the form and use a function like this:
const reset = () =>{setField(initialField)}
This reset function will not work. Once the value of initialField is unified to the current value of field, setField will always contain the current value of field.
Lets say we replace initialField with:
initialField.map((e)=>{return e;})
The map method is immutable. It should create a separate array. However, replacing initialField with a map method of initialField does not change the result. useState still unifies the value of initalField with field.
Using the separator operator, {...intialField}, does not change the outcome. Neither does using intialField.concat(). Lets say we assign initalField to another array resetField. React will unify together all three arrays: field, initialField and resetField.
Lets say we hard code the array into the setState function. Assuming we have an array of N objects, it would look something like:
const [field, setField] = useState([{object 0}, {object 1} ... {object N}] );
The reset function is still:
const reset = () =>{setField(initialField)}
The reset function will work exactly once. Once the code is executed, React unifies initialField with field, so the reset function will no longer work. Lets say we replace initalField with a map method, a separator operator or a concat method. React will still unify the field array with initialField.
A work around this is to hard code the array into the setState and the reset functions as follows:
const [field, setField] = useState([{Object 0},{Object 1},...{Object N}]);
const reset = () =>{setField([{Object 0},{Object 1},...{Object N}])};
This is an ugly solution that complicates maintenance as the same changes have to be made twice.
I have an Array of strings which I filter though an input. The value of this input is watched, and the new filtered Array is created upon changes in the input value. This works fine.
Independently of this I have a switch toggle which changes the way the filtering works, by modifying the function in the watch.
My problem: toggling this switch is not taken into account until the moment the input changes again. This is normal and expected: the watched value did not change.
Is there a way to :
either force the execution of a watch function (in which case I would also watch the toggle)
or use an OR construction in the watch ("if either the input or the toggle change, do this and that...")
What do right now is to have one extra method that does the filtering, that is called on the input or slider change. This works but I am wondering whether there is a better way to cleverly slim down the code.
One way would be to use computed property to return filtered Array. No need to use watch. Computed properties are just for that occasions 😉
If you want to use watch:
create a method that does the filtering.
Fire this method in a watcher
Fire it when toggle change.
Two working solutions, but computed would work better.
You definitely should use a computed property instead of watch in this case.
computed: {
computedArray() {
let result = [];
// your filtering code that include switch toggle
return result;
}
}
I'm trying to build a micro JS library to emulate InDesign text threading on the web using this constructor pattern boilerplate. The idea is the user feeds the function DOM selectors in a desired order, e.g.
var thread=new TextThread(['#container1','#container2']);
thread.build();
And from there the library splits the text contents of the first container into spans for each word. When a word is out of view, the library will move it to the following container in the order.
Here's where IntersectionObserver comes in: I need to assign an observer to each container fed to the constructor, and then have it observe the spans inside it. When a span goes out of view, it fires the observer callback, which should loop through all the observers with the .takeRecords() method to check which spans are out of view in each container. I've gotten the observers to fire their callbacks with no problem, but the issue is referencing all the observers within the callback.
What I've tried is storing an array variable in the self-executing function from the boilerplate, and then when the constructor is built, it pushes the observers to that array.
var observers=[];
var Constructor = function (selectors) {
/*placeholder for code that selects and assigns the containers*/
containers.forEach((item, i) =>{
var options={
root: item,
rootMargin: '0px',
threshold: 1.0
}
var newObserver=new IntersectionObserver(callback,options);
observers.push(newObserver);
})
};
Then when the text is split into spans by word
words.forEach((word,w) =>{
observers[ current container index ].observe(word); });
In the callback, entries for the observer that fired the callback are visible. However, if I try to reference the other observers using my observers array variable, takeRecords() returns an empty array. Here's the callback I'm testing:
function callback(entries){
//the entries for the observer firing the callback are returning correctly
console.log(entries)
//the forEach below doesn't work though. It returns an empty array for each observer.
observers.forEach((item, i) => {
console.log(item.takeRecords())
});
}
In the callback, entries for the observer that fired the callback are visible. However, if I try to reference the other observers using my observers array variable, takeRecords() returns an empty array - even for the observer that fired the callback!
It's working fine, as documented:
takeRecords() returns an array of IntersectionObserverEntry
objects, one for each targeted element which has experienced an
intersection change since the last time the intersections were checked,
either explicitly through a call to this method or
implicitly by an automatic call to the observer's callback.
Note: If you use the callback to monitor these changes, you don't need to
call this method.
(Emphasis mine)
you declared the observers as a private variable for the class. You either need to make it public, or static. I am not familiar with your design but I would suggest to make it static. From an architectural point of view making those static makes more sense for me
I have a React component that includes an input element. When the value of the input changes the handler function is supposed to validate the input and act accordingly. The actual validator is dynamically created (via new Function(code)) with the actual code passed as a string prop to the element. The validation code never changes for a given component.
Right now I am doing the function construction in the actual onChange handler which seems unnecessary. It does not belong in state either. I would like to create the function once, store it somewhere and use it on demand. My question is, where should it be stored? Should I make it an attribute of the actual component object? The statics object seems also reasonable, but can one pass properties dynamically (like the code string above) and if yes, how?
Recomputing the validator inside the onchange is not that bad from a reproducibility point of view. It only affects performance and if that is an issue one possibility would be to use the caching mechanism of your choice:
handleOnChange: function(){
if(this.cachedValidatorString !== this.props.validatorString){
this.cachedValidatorString = this.props.validatorString;
this.cachedValidator = new Function(...);
}
// ...
}
Another, perhaps cleaner, approach would be to update the validatorFunction field inside the render method. That is the earliest you can update it and it guarantees that it will always correspond to your current props.
render: function(){
this.validatorFunction = new Function(this.props.validatorString);
return <input onChange={...} />;
}
As for the other possibilities you mentioned:
I agree that this.state is not the best place to put the validator function because in general you want to avoid putting things that can be computed from the props in the state. See props in getInitialState as an anti pattern
Finally, I don't think statics would make much sense here. Static properties are shared by all instances of your class but the validator functions need to be different for each instance of the component.