React 16.4 ComponentDidMount not respecting strict equality checks - javascript

The issue
Hello I'm running into a bit of a proplem using ComponentDidMount.
I wish to perform a network request if the value is not undefined, it will be undefined when the component mounts, the value will be passed when a user is redirected by using the state that lives inside of location which I will passed to the component usinghistory.push(route, state);
Consider the code below, I only posted the code in question since the other lines are irrelevant to the issue.
As you can see below I am making a network request if the data I'm performing an equality check on is not undefined, this indeed works when the user redirects because the value exists since the user performed an action which calls history.push(route, state); passing the route and the desired data to be passed, however if the user visits the component without first being redirected by using history.push(route,state) it will be undefined by default, in this case I'm checking to see if the value is defined or not, which should perform the equality check and not execute anything at all, instead the console throws me an error pointing to my logic for the issue, does ComponentDidMount respect equality checks for undefined properties?
componentDidMount() {
this.loadData();
if (this.props.location.state.editData !== 'undefined') {
const { asOfDate } = this.props;
const { editData } = this.props.location.state;
const { batchName, fileName } = editData;
this.getFile(asOfDate, fileName, batchName );
}
}
this is the error I am getting
Cannot read property 'editData' of undefined
at SingleTransactionContainer.componentDidMount

You are trying to access a key in an undefined object.
So you should assure that you can access the value of your key.
The best way to do it is:
(((this.props || {}).location || {}).state || {}).editData
which will return undefined if you are not receiving your props.
To resume, the problem is in your if condition and also undefined not "undefined" because if you compare
undefined === "undefined" --> false

Add below condition
if (this.props.location && this.props.location.state && this.props.location.state.editData !== 'undefined') {
const { asOfDate } = this.props;
const { editData } = this.props.location.state;
const { batchName, fileName } = editData;
this.getFile(asOfDate, fileName, batchName );
}

Related

I’m having trouble figuring out the logic in my custom hook

Basically, as you can see in the code, I only want to run one of these API requests. If origCompId is passed, it runs duplicateInstance, otherwise, it runs addNewInstance. origCompId is a query parameter so it takes a second for it to update. This causes addNewInstance to run because origCompId begins as null. I feel like the logic is simple here, but for some reason, I’m just not seeing the solution.
I tried adding the hasRendered ref, as you can see, but that didn’t prevent it from happening. The problem is, that I need this hook to run when the app first loads. I can’t separate these API calls. If the user is loading the app for the first time, it’ll run addNewInstance and load their initial data. If the user duplicates their existing app, it’ll have origCompId and run duplicateInstance.
Any help would be much appreciated!
import { useEffect, useRef } from "react";
import { addNewInstance, duplicateInstance } from "../util/services";
const useAddInstanceToDB = (instance, compId, origCompId) => {
const hasRendered = useRef<boolean | null>(null);
useEffect(() => {
const controller = new AbortController();
if (hasRendered.current) {
if (instance && compId && origCompId) {
duplicateInstance(instance, controller, compId, origCompId);
} else if (instance && compId) {
addNewInstance(instance, controller, compId);
}
}
return () => {
controller.abort();
hasRendered.current = true;
};
}, [instance, compId, origCompId]);
};
export default useAddInstanceToDB;
I think you can set the initial props to null / undefined so that non of the statement will run before your api completes
useAddInstanceToDB(undefined, undefined, undefined);
const useAddInstanceToDB = (instance, compId, origCompId) => {
useEffect(() => {
const controller = new AbortController();
if (origCompId) {
if (instance && compId && origCompId !== -1) { // will not run when hook is inited
duplicateInstance(instance, controller, compId, origCompId);
} else if (instance && compId && origCompId === -1) { // also will not run when hook is inited, only run when instance and compId is defined
addNewInstance(instance, controller, compId);
}
}
return () => {
controller.abort();
};
}, [instance, compId, origCompId]);
};
setOrigCompId to -1 if new instance is needed, or any value other than -1 if duplicate is needed
Possible to have better solutions and I am open for that
You want to run your side effect exactly when your component gets information about origCompId whether it exists or not. Right now your component just can't know when the data is loaded - so you can't do anything. So you need to tell it to your component - you can set origCompId = -1 if it not exists (as suggested by previous author), but I advise you to create a new variable that will tell your component that all the data has loaded.

problems with variable value that initializes with wrong value not following condition

I am trying to make a method that checks if my screen is in share mode, so I made a logic that if my variable this.share is TRUE, it is shared, but if the variable this.share is FALSE, the screen is not shared.
When loading the screen, the method is called, and even though my condition is correct, the return of the variable is wrong, the return only appears as correct, after the screen loads and I perform some action like clicking a button for example, I think it may have to do with loading, or asyncs functions.
For example, the expected return is TRUE, but when loading the variable the return is FALSE and after an action the variable returns TRUE,
declaration:
this.shareKey
method to check.
this.router.events.subscribe((event: any) => {
let r = this.route;
while (r.firstChild) {
r = r.firstChild;
}
r.params.subscribe((param) => {
this.shareKey =
param.secretkey != null || param.secretkey != undefined;
});
});
I tried to do this by calling it in a service, but it didn't work.

Uncaught Invariant Violation on saving to redux store second time

I'm relatively new to React-Redux and was facing an issue where the if i update the state of a reducer param for the first time, it executes perfectly, then if i execute it again with some different value it throws this exception in my try/catch block :
Invariant Violation:
A state mutation was detected between dispatches, in the path "object1.object2.0.object3.0.value"
Now I've added another update for the state whenever it catches the exception, and after the first exception, the states are updated accordingly and doesn't give any exception for any further updates.
Previously it was happening for every update call but following a few solutions online, now it only happens when the value is updated for the second time. Here is the code snippet that I'm using to update my state.
let tempObject3 = [
{
name : "param1",
value = "",
}
{
name : "param2",
value = "",
}
{
name : "param3",
value = "",
}
];
const component = (props) = {
//this state variable is updated every time there is a change in the form
const [formState, setFormState] = React.useState({
value1 : "",
value2 : "",
value3 : "",
});
const updateState = async () = {
const temp = async () = {
// have used other cloning libraries like just-clone and spread operator and JSON.parse(JSON.stringify(object))
let object2Copy = _.cloneDeep(props.object2);
for(var i=0; i<object2Copy.length; i++)
{
let object2ElementCopy = _.cloneDeep(object2Copy[i]);
if(object2ElementCopy.name === "desiredName")
{
//code for populating tempObject3, with modified data from formState state variable
if('object3' in object2ElementCopy)
{
let object3Copy = _.cloneDeep(object2ElementCopy.object3)
object2ElementCopy = {...object2ElementCopy, object3 : tempObject3}
}
else
{
object2ElementCopy.object3 = tempObject3;
}
object2Copy[i] = object2ElementCopy
}
}
return object2Copy;
}
try
{
props.updateObject2(await temp());
}
catch(e)
{
//update after which no further exceptions are caught
props.updateObject2(await temp());
console.log(e);
}
}
}
Some methods and objects used here are passed to props via MapDispatchToProps and MapStateToProps.
The exception only arises when I update it for the second time, although the same reducer is working fine in other components without the need for async and await.
Yes i know the logic should be in the reducer but I've even tried doing that and the same exception keeps happening. I've even created a new object in the reducer to save this data to which has only one level, still it was giving me the same error.
So anyone who will face this error in the future, let me help you not waste a lot of time, and bang your head against the table.
The problem here is that i was getting the error the second time after updating the object3 right. What was happening every time in the code was that i was using the same tempObject3 to write new values with.
Now why was it giving an error the second time? because the first time i updated it in redux, that object created a reference to the redux store, now every other time i try to update it, the same reference was used and a new tempObject3 wasn't created, hence the Invariant Violation .
So anyone who face this error or something similar, please do this before updating the value of the object again :-
tempObject3_copy = _.cloneDeep(tempObject3);
//logic for updating tempObject3Copy with new values
// update to redux store after updating object with new values
Hope this collective effort for the solution will be helpful :)

React, check state if key pair exists depending on parameter

So I'm trying to figure out how to use kind of an all-encompassing function to reduce bloat in my application. I've got a bunch of dialog windows that are handled via state, similar to this:
toggleSettingsDialogue = () => {
this.setState({settingsOpen: !this.state.settingsOpen});
}
I'm trying to reduce this function, which is repeated for each additional dialog, into one. My thought is to pass in two parameters - one for the dialog that's meant to be opened, and another that defines the state of that dialog - either true or false.
The issue is, I'm stuck on figuring out how to check if the first parameter passed (i.e. the name of the dialog window in state) exists or not.
Let's say we've got a state with...
state = {
diagSettingsOpen: false,
diagAddItemOpen: false
}
How would I check to see if any string passed as a parameter inside the function is actually there or not, and subsequently use that key to set state if it matches?
toggleSettingsDialogue = key => {
if(key in this.state)
this.setState(({[key]: val}) => ({[key]: !val}));
}
Here's how you can check the same -
let state = {
diagSettingsOpen: false,
diagAddItemOpen: false
}
function setState(stateName, value) {
if (state.hasOwnProperty(stateName)) {
state[stateName] = value;
} else {
console.log("invalid state");
}
}
setState("diagSettingsOpen" ,true);
console.log(state);
setState("diagSettingsClose" ,true);

Overwrite getter logic for an instance of Immutable.js

I am using immutable.js to manage configuration object, e.g.
config.js
export default Immutable.fromJS({
foo: 'FOO',
bar: {
// ...
}
});
I would like to overwrite the getter functions so that accessing an undefined property would throw an error.
How do I do it given that every setter method of the resulting object will create a new instance of Immutable (in effect overwriting whatever monkey-patching)?
Generally I do not want it to throw an error, just handle undefined without causing the code to break fatally. To throw specific error I might use try/catch, but this is highly inefficient.
To prevent breakage I do something like this.
My motivation here is mostly that my call .get of undefined poops itself really hard, and initializing properly all over the place helps, but doesn't catch all edge cases. I just want the data or undefined without any breakage. Specific type checking causes me to do more work later if I want it to make changes.
This looser version solves many more edge cases(most if not all extend type Iterable which has .get, and all data is eventually gotten) than a specific type check does(which usually only saves you when you try to update on the wrong type etc).
/* getValid: Checks for valid ImmutableJS type Iterable
returns valid Iterable, valid Iterable child data, or undefined
Iterable.isIterable(maybeIterable) && maybeIterable.get(['data', key], Map()), becomes
getValid(maybeIterable, ['data', key], Map())
But wait! There's more! As a result:
getValid(maybeIterable) returns the maybeIterable or undefined
and we can still say getValid(maybeIterable, null, Map()) returns the maybeIterable or Map() */
export const getValid = (maybeIterable, path, getInstead) =>
Iterable.isIterable(maybeIterable) && path
? ((typeof path === 'object' && maybeIterable.getIn(path, getInstead)) || maybeIterable.get(path, getInstead))
: Iterable.isIterable(maybeIterable) && maybeIterable || getInstead;
//Here is an untested version that a friend requested. It is slightly easier to grok.
export const getValid = (maybeIterable, path, getInstead) => {
if(valid(maybeIterable)) { // Check if it is valid
if(path) { // Check if it has a key
if(typeof path === 'object') { // Check if it is an 'array'
return maybeIterable.getIn(path, getInstead) // Get your stuff
} else {
maybeIterable.get(path, getInstead) // Get your stuff
}
} else {
return maybeIterable || getInstead; // No key? just return the valid Iterable
}
} else {
return undefined; // Not valid, return undefined, perhaps should return false here
}
}
Just give me what I am asking for or tell me no. Don't explode. I believe underscore does something similar also.

Categories