I keep getting payload undefined when destructuring:
let videosArray = [];
if (payload.videos) {
const {
payload: { videos }
} = action;
videosArray = videos;
}
return videosArray;
How can I check for undefined? I have tried the check below, but keep getting the error:
if (typeof payload !== "undefined") {
You just have to make sure the action is defined first, and access its parameters instead of calling payload by itself. In your example it looks as if payload was undeclared when you try to access it
function getVideos(action) {
if (action && action.payload && action.payload.videos) {
const {payload: {videos}} = action;
return videos;
}
return [];
}
console.log(getVideos()); // action undefined, expected []
console.log(getVideos({})); // payload undefined, expected []
console.log(getVideos({payload: {}})); // videos undefined, expected []
console.log(getVideos({payload: {videos: [1,2,3]}})); // expected [1,2,3]
Of course if you really wanted to complete the task with just destructuring you could simply create some default values like this:
function getVideos(action) {
const {payload: {videos=[]}={}} = action || {};
return videos;
}
console.log(getVideos()); // action undefined, expected []
console.log(getVideos({})); // payload undefined, expected []
console.log(getVideos({payload: {}})); // videos undefined, expected []
console.log(getVideos({payload: {videos: [1,2,3]}})); // expected [1,2,3]
You need to have action defined like this const action = {payload: {videos: ["Matrix", "Star Wars"]}}
And you can check that payload is not undefined like this if(action && action.payload) then you could do your destructing operation.
You could take a check and a default value without destructuring.
function getVideos(action) {
return action && action.payload && action.payload.videos || [];
}
console.log(getVideos()); // action undefined, expected []
console.log(getVideos({})); // payload undefined, expected []
console.log(getVideos({ payload: {} })); // videos undefined, expected []
console.log(getVideos({ payload: { videos: [1, 2, 3] } })); // expected [1, 2, 3]
Related
I'm trying to show a success feedback once the users are registered without error. There is an errors redux reducer object which stores errors that come from the back end. And the if statement is supposed to give the success feedback if the errors object is empty, it displays the errors if there are any, it also registers the users if there is none, but it doesn't seem to execute the if(props.errors === {}) block.
function onSubmit(e) {
e.preventDefault();
const newUser = {
fname: fname,
lname: lname,
phone: phone,
email: email,
password: password,
};
dispatch(register(newUser));
if (props.errors === {}) { //This block is not executed
setSnackOpen(true);
setFname('');
setLname('');
setEmail('');
setPhone('');
setPassword('');
}
console.log('errors :' + props.errors); //logs 'errors : [object, object]'
}
The errors reducer:
import { GET_ERRORS } from '../actionTypes/actionTypes';
const initialState = {
};
export const errorReducer = (state = initialState, action) => {
switch (action.type) {
case GET_ERRORS:
return action.payload
default:
return initialState;
}
};
Because you are comparing props.errors with {} new empty object so even though both are empty they are pointing to different memory location so if condition will return false for the statement if(props.errors === {})
You can check length of the props.errors like
if(!Object.keys(props.errors).length)
Every time you declare a new object(empty or not), it will be created with a new reference. you can run below code in you dev console. all of them will give the result false. Because obj1 is a different empty object than obj2.
var obj1 = {}
var obj2 = {}
console.log(obj1 === {})
console.log(obj2 === {})
console.log(obj1 === obj2)
false
false
false
One way to check if an object is empty is this:
Object.keys(obj1)
This will give you an array of key values from object.
so, you can check the length of the array.
const keysArray = Object.keys(obj1)
if (keysArray.length === 0){
// do something
}
This if (props.errors === {}) would never be true, as it's actually comparing their references, which are not the same.
And I would do as below, and the advantage is that it would work both for when there isn't any key, as well as for when there are some, but their values are empty.
if (!Object.keys(props.errors).some((k) => props.errors[k]))
It's helpful for when you consider {firstNameError:"", lastNameError:"" } and {} as empty. See the snippet below:
const objOne={firstNameError:"", lastNameError:"" };
const objTwo= {};
if(!Object.keys(objOne).length){
console.log("First console prints empty")
}
if (!Object.keys(objOne).some((k) => objOne[k])){
console.log("Second console prints empty")
}
if(!Object.keys(objTwo).length){
console.log("First console prints empty")
}
if (!Object.keys(objTwo).some((k) => objTwo[k])){
console.log("Second console prints empty")
}
I have an array of objects like:
[{}, {}, {}, {}, {}];
I also have a function that is recursive and accepts a function as a parameter:
function recursiveFunction(getResponse) {
const result = getResponse();
const conditionMet = check(result);
if(conditionMet) {
return result;
}
return recursiveFunction(getResponse);
}
How can I create a function getResponse so each time that it is called in recursiveFunction it will return the next iteration of my array?
For example upon each recursion it should be getting the next object in the array:
function getResponse(index) {
return array[index ++];
}
And when I call it:
const firstIndex = -1; // Because index ++ in function
const result = recursiveFunction(getResponse(firstIndex));
I understand why it is only returning the first value but I'm not sure how I can modify it to return the next index in the array when calling it again.
The way to do this in JavaScript would be to use the built-in Iteration Protocol -
const myinput =
[ {a:1}, {b:2}, {c:3}, {d:4} ]
const it =
myinput.values()
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())
{value: {a:1}, done: false}
{value: {b:2}, done: false}
{value: {c:3}, done: false}
{value: {d:4}, done: false}
{value: undefined, done: true}
You could make your own iter function which returns a function like the one you describe -
function iter(iterable) {
const it = iterable.values()
return () => it.next().value
}
const inputA = [1,2,3]
const inputB = ["a","b","c"]
const nextA = iter(inputA)
const nextB = iter(inputB)
console.log(nextA()) // 1
console.log(nextA()) // 2
console.log(nextB()) // "a"
console.log(nextB()) // "b'
console.log(nextA()) // 3
console.log(nextB()) // "c"
console.log(nextA()) // undefined
console.log(nextB()) // undefined
In your program, you will have to check for undefined to know when there are no values left -
function recursiveCheck(check, getResponse) {
const result = getResponse()
if (result === undefined)
return "not found"
else if (check(result))
return "condition met"
else
return recursiveCheck(check, getResponse)
}
const output = recursiveCheck(myChcek, iter([{...}, {...}, ...]))
console.log(output)
Using recursion in this particular cases means you the size of your input will be limited. If you simply use for..of all of your problems go away -
function iterativeCheck(check, iterable) {
for (const result of iterable)
if (check(result))
return "condition met"
return "not found"
}
const output = iterativeCheck(myCheck, [{...}, {...}, {...}, ...])
console.log(output)
See also Symbol.asyncIterator for how this approach can be used on async iterables.
You should be passing the function as the argument, not calling the function.
const result = recursiveFunction(getResponse);
Also, if you initialize the index as -1, you need to use ++index, not index++.
I normally can do this when I render the react component with an isLoading however if I try to use that to test that a variable is ready it complains that the map is undefined even though in the console it certainly is an array value that I am selecting. The isLoading returns undefined sometimes, and I've tried checking for if isLoading is !undefined and so here I am stuck.
const { leadsBuilder, isLoading } = useTracker(() => {
const noDataAvailable = { leadsBuilder: [] };
if (!Meteor.user()) {
return noDataAvailable;
}
const handler = Meteor.subscribe("leadsBuilder");
if (!handler.ready()) {
return { ...noDataAvailable, isLoading: true };
}
const leadsBuilder = LeadsBuilderCollection.findOne({ _id: params._id });
return { leadsBuilder };
});
if (!isLoading && leadsBuilder) {
let stateReplace = [];
leadsBuilder.inputs.map((leadInput, i) => {
stateReplace.push({ id: i, text: leadInput.name });
});
}
You're testing if leadsBuilder is truthy, but your invoking the .map on a property of leadsBuilder.
You should replace your condition by:
if(!isLoading && leadsBuilder?.inputs?.length) {
//your code
}
The ? test if the variable / property is defined, and then we use length because we want the array to not be empty
In the computed propertys I am trying to match an ID received from an API to an array of objects with ID keys also received from an API and retrieve the name key from the matching ID object.
the obj variable occasionally throws an "obj is undefined" error but not consistently.
I think its to do with the IDs been async. Changed the function from arrow to classic function to not mess with the this scope.
data() {
return {
upComing: [],
popular: [],
topRated: [],
playingNow: [],
details: [],
genres: []
}
},
computed: {
genre: function() {
let list = this.upComing[0] ? this.upComing[0].genre_ids[0] : 0
let obj = this.genres.find(function(o) {
return o.id === list
})
return obj.name
}
},
created() {
let self = this
APIService.getUpcoming()
.then(response => {
self.upComing = response.data.results
//console.log(this.upComing)
})
.catch(error => {
console.log(`There was an error: ${error.response}`)
}),
APIService.getGenres()
.then(response => {
this.genres = response.data.genres
//console.log(this.genres)
})
.catch(error => {
console.log(`There was an error: ${error.response}`)
})
}
}
I get this TypeError: "obj is undefined" and this [Vue warn]: Error in render: "TypeError: obj is undefined"
and it throws them each twice. So i have 4 errors in the console but its just these 2 twice with a 1 second delay.
The error is that this.genres is [], so the computed property is computed when your component is mounted, so the result of this.genres.find(.... is undefined (because it can't find something in an empty list).
You can have a default value by using the || notation like this:
let obj = this.genres.find(function(o) {
return o.id === list
}) || { name: 'defaultName'}
This means that if nothing is found on the genres list, you still have a default result, then you can return obj.name with out having the error.
Also note that the genres variable is empty because the computed method is tun before your promise is resolved, and it runs again after you update that variable
You're most likely right about the issue being the async stuff, couldn't you just safeguard against undefined by doing something like this:
computed: {
genre: function() {
let list = this.upComing[0] ? this.upComing[0].genre_ids[0] : 0
let obj = this.genres.find(function(o) {
return o.id === list
})
return obj ? obj.name : '' // or whatever should be the default
}
},
I have an array containing one object of this form :
Array = [ { type: type, message: message } ]
I keep getting ESLint errors asking me to use object destructuring and array destructuring.
Currently my code looks like this :
let type=null;
let message=null;
if (data.length > 0) {
({ type, message } = data[0]);
}
So far this works and my variables are assigned correctly, however I am still getting the "Use array destructuring" message from ESLint.
Any help with this would be appreciated. Thank you
You can destructure the array:
let type=null;
let message=null;
if (data.length > 0) {
[{ type, message }] = data;
}
The code above is a shorter version of:
[ firstElement ] = data; // array destructruring
({ type, message } = firstElement); // object destructuring
Faly's way is good. You can also use default values when destructuring:
function test(label, data) {
// 1 -----------------------------vvvvv
let [{type = null, message = null} = {}] = data;
// 2 -----^^^^^^^---------^^^^^^^
console.log(label, type, message);
}
test("test1: ", []);
test("test2: ", [{type: "t"}]);
test("test3: ", [{type: "t", message: "m"}]);
That works because if data.length is 0, data[0] is undefined, and so triggers use of the default value {} (1) for the array part of that; within the object part of that, we use null (2) to handle any missing values on the object as well.
EsLint wants you to write
let type = null;
let message = null;
if (data.length > 0) {
[{ type, message }] = data;
}
which destructures the first item of an iterable data into the {type, message} target. (More items are ignored).
I would however recommend to use default values for the empty case:
const [{type, message} = {type:null, message:null}] = data;
or also
const [{type = null, message = null} = {}] = data;