I'm using react with typescript and was getting Object is possibly 'undefined' error when return language.toLowerCase().includes(selectedCategory) so I added a checking as in below. Error is gone but I'm not sure whether It can impact the performance. Please advice.
import { filter } from 'lodash';
...
return filter(channels, ({
language
}: Channels) => {
if (
language &&
language.toLowerCase() !== selectedCategory &&
selectedCategory !== 'all'
) {
return false;
}
return (
language && language.toLowerCase().includes(selectedCategory)
);
});
You can use Optional Chaining instead.
If the language is not null or undefined (nullish) it will access the property or method you want otherwise it will return undefined
If you're damn sure about language that it can't be null or undefined then use ! operator (non-null assertion operator) and rewrite your code like this:
import { filter } from 'lodash';
...
return filter(channels, ({
language
}: Channels) => {
return language!.toLowerCase().includes(selectedCategory);
});
Or You can use Optional Chaining instead.
No, that will not impact performance in any way, that's an incredibly inexpensive and common type of check to performance.
You can simplify it though with optional chaining
If you write language?.toLowercase(), it will evaluate to undefined if language is undefined, and a lowercase language if it is defined. So:
if (
language?.toLowerCase() !== selectedCategory &&
selectedCategory !== 'all'
) {
return false;
} else {
return language?.toLowerCase().includes(selectedCategory)
}
If you are sure that the object value is never null or undefined, You can use ! mark:
language!.toLowerCase().includes(selectedCategory)
If you are sure that language will never be undefined, you can use the ! non-null assertion operator to remove that warning.
language!.toLowerCase()
This is the best option in terms of runtime performance since when the code is transpiled into JavaScript the assertion operator is just removed.
Related
I have a custom hook that returns a value of useRef in my TS app. Unfortunately it is complaining about the type of the ref I am returning and I have no clue how to properly type this.
This is what my hook looks like:
interface MyHookInterface {
someBooleanState: boolean
myRef: HTMLElement | null
}
const useMyHook = (): MyHookInterface => {
const [someBooleanState, setSomeBooleanState] = useState<boolean>(false)
const myRef = useRef<HTMLElement | null>(null)
useEffect(() => {
const el = myRef.current // what type should this be?
// Complaining that el possibly undefined
if(el?.offsetWidth < el?.scrollWidth){
// do stuff
}
}, [])
return {
someBooleanState,
myRef, // Getting: Type 'MutableRefObject<HTMLElement | null>' is missing the following properties from type 'HTMLElement': accessKey, accessKeyLabel, autocapitalize, dir, and 234 more
}
}
As you can see in the comments, my hook has a few errors relating to typing:
1- Do not know how to type myRef in the interface. Keep in mind that it is to be used on multiple types of HTML elements, so I can not specify here whether it will be a div or what.
2- Do not know how to type el, but the access to its properties is complaining about it being undefined
How can I properly type these values in my hook?
This happens because a ref's value is stored under the ".current" property.
i.e.
const Comp = () => {
...
const refVal = React.useRef(2);
console.log(2 === refVal); // false
console.log(2 === refVal.current); // true
...
}
The solution depends on your intention - if you want to return the ref itself, change the type of the interface to MutableRefObject<HTMLElement | null> as suggested by the type error, otherwise, replace the return value with:
return {
someBooleanState,
myRef: myRef.current,
}
You may be getting undefineds if el is not yet defined (i.e. if it hasn't been assigned yet) due to you accessing the property using the ?. operator (which, to be clear, is correct.
e.g.
null?.test === undefined; // true
To solve this, check if el is defined and optionally (though this is not required by the interface's definition), check that both values are defined and are numbers (i.e. using el && !isNaN(el?.offsetWidth) && !isNaN(el?.scrollWidth) && el.offsetWidth < el.scrollWidth. Alternatively, use the nullish coalescing operator if this is applicable for your use case, i.e. (el?.offsetWidth ?? 0) < (el?.scrollWidth ?? 0).
The type of a ref is not just the object that it's referencing. It's a React.MutableRefObject that wraps what it's referencing. This is what provides the current property so the ref can work.
If you muse over myRef you should see the type you need. In this case:
React.MutableRefObject<HTMLElement | null>
Which makes your hooks return type this:
interface MyHookInterface {
someBooleanState: boolean
myRef: React.MutableRefObject<HTMLElement | null>
}
Secondly, the reason that this does not work:
const el = myRef.current // what type should this be?
// Complaining that el possibly undefined
if(el?.offsetWidth < el?.scrollWidth){
// do stuff
}
Is because your ref may not have been assigned a value yet. which means that el?.offsetWidth is undefined, because el is still null. And undefined is not a valid operand in a < comparison. (if (undefined < undefined) doesn't make much sense)
You can easily fix this by checking to make sure that el exists before you do the comparison:
if (el && el.offsetWidth < el.scrollWidth){
// do stuff
}
Working example
I'm learning React and I was getting this warning:
Array.prototype.map() expects a value to be returned at the end of arrow function array-callback-return
I cleared it by adding a return true outside of the if/else if statement, i.e.:
arr.map(foo => {
if (foo.attr === 'someKey') {
return baz
} else if (foo.attr === 'someOtherKey') {
return bar
}
return true //adding this return value clears the warning
})
Would this create any problems, or is this an okay way to clear that warning? I'm learning React so I just want to make sure I'm not developing any costly bad habits.
You don't need to return true for your piece of code. You can write your callback implementation inn a different way so that your linter doesn't give you the false warning
arr.map(foo => {
if (foo) {
return baz;
}
return bar;
})
In the above code, return bar will only execute if the if condition was false otherwise your code will return early and not reach return bar
UPDATE:
If you have a if-else-if rule, you can return undefined from the map function and filter out undefined values using filter(Boolean). It is needed you map needs to return something and if none of your conditions map, there is on return value specified
arr.map(foo => {
if (foo.attr === 'someKey') {
return baz
} else if (foo.attr === 'someOtherKey') {
return bar
}
return;
}).filter(Boolean);
The warning is because you still had not specified what to return when foo.atr is neither someKey or someOtherKey. Returning true specifies what to return in this case. But it could be any valid value
certificate?: any;
<S.Contents>{certificate[0]}</S.Contents>
<S.Contents>{certificate[1]}</S.Contents>
<S.Contents>{certificate[3]}</S.Contents>
If the type of props is set to any and used as an index of an array, it works well. But if you change that any type to Array, Generic type'Array' requires 1 type argument(s). I get an error like this.
When I print typeof to the console, the object comes out, so when I change it from Array to object, I get an error other. "Object is possibly 'undefined'."
setCertificate(res.data.qualification.certificate);
Since it is an original array, I tried to create elements by turning it into map, but it also didn't work with a map is not a function error. I don't know what to do with me. Help.
You Always have to check for possible null or undefined values.
This is how I would do it to make it 100% safe
return (<div> {
certificate && certificate.length > 0 ? (
certificate.map((item, index) => (
<S.Contents key = {index}> {item}</S.Contents>
))
) : null
} </div>)
You get this error because you used an optional property for certificate. You need to add a check for undefined, to make sure it is actually defined.
Assuming your react function looks something like this, this would be a fast way to solve your issue:
function ReactComponent(props: {
certificate?: any[]
}) {
const certificate = props.certificate || [];
return (
{certificate.map((certificateItem) => (
<S.Contents>{certificateItem}</S.Contents>
))}
);
}
This line const certificate = props.certifate || []; assigns the first value if it is not undefined to the variable certificate otherwise the second. An if statement would also work but would be more verbose in this case:
function ReactComponent(props: {
certificate?: any[]
}) {
let certificate = props.certificate;
if(typeof certificate === "undefined") {
certificate = [];
}
return (
<div>
{certificate.map((certificateItem) => (
<S.Contents>{certificateItem}</S.Contents>
))}
</div>
);
}
What should be the right way to declare a function in order to filter an array of objects?
It could be a method in a class, but it becomes messy when parameters need to be added.
this.works = this.base_works.filter(whatever,{some : value});
whatever(x){
return x.value !== this.some;
}
Im not sure that declaring the function on the same general class is clean.
Any thoughts?
Thanks!
Ok I tried the two options, arrow function
var myFunction = (x) => {
return x.category !== undefined && x.category.indexOf(this.current_filter) !== -1;
}
And
normal
var myFunction = function (x) {
return x.category !== undefined && x.category.indexOf(this.current_filter) !== -1;
}
When I called the the first one, the reference this is to the object. On the latter the reference on this, is just him and his arguments.
The second one is the one that is perfect to use on the array callbacks. Thanks suraj to help me to find the solution!
I'm trying to filter an array in react. Here's my code:
resultArray = myArray.filter((item) => {
return item.children.length === 0;
});
This gives me an eslint error:
Unexpected block statement surrounding arrow body
So I traded the braces for parentheses:
resultArray = myArray.filter((item) => (
return item.children.length === 0;
));
This gives me an unexpected token error while highlighting the return.
What's the right way to do this?
Because is a single expression, you can do the following:
resultArray = myArray.filter((item) => item.children.length === 0);
yes as #DanielSchneider already says:
you can use a shorthand (or also called lambda) arrow function as follows:
resultArray = myArray.filter(
item => item.children.length === 0 //this is the lambda function
);
because it is a single expression and a return value (even if the return value is undefined) you can use the short hand arrow function. It will always return the result of the expression (even undefined).