What is the right import for lodash constructor? - javascript

I am working on some Angular projects and use lodash as helper. I read a lot of articles about how to import lodash the right way to make the bundle size smaller. But I got a problem with my current implementation because I use lodash chaining method.
return _(items)
.groupBy(x => x.vote.code)
.map(items => items)
.value();
import * as _ from 'lodash';
I read import * is not good.
My problem is how can I import _(items) so that my import looks like below?
import groupBy from 'lodash/groupBy';

If you want to reduce your bundle size, you want to avoid importing the whole of Lodash. Instead, you can just import the functions you want - groupBy and map.
However, in this case you are also using chaining, which does require all of Lodash. You can re-write your code using flow and use the functional programming-friendly bundle of Lodash to replace the chaining:
import {flow, groupBy, map} from 'lodash/fp';
/* ... */
const chainReplacement = flow(groupBy(x => x.vote.code), map(items => items));
return chainReplacement(items)
Here is a quick illustration that the two produce equivalent results:
const {flow, groupBy, map} = _;
//sample data
const items = [
{ vote: { code: "1" }, name: "Alice"},
{ vote: { code: "2" }, name: "Bob"},
{ vote: { code: "1" }, name: "Carol"},
{ vote: { code: "2" }, name: "Dave"},
{ vote: { code: "1" }, name: "Edel"}
];
//using literally the same functions
const groupByCallback = x => x.vote.code;
const mapCallback = items => items.length;
const chain = _(items)
.groupBy(groupByCallback)
.map(mapCallback);
const chainReplacement = flow(
groupBy(groupByCallback),
map(mapCallback)
);
console.log("chain", chain.value())
console.log("chainReplacement", chainReplacement(items))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash-fp/0.10.4/lodash-fp.min.js"></script>
I made some data that matched your usage of groupBy but then had to modify the map callback because it didn't make sense. I assume because it was for illustrative purposes. So I swapped it for items => items.length to get the count of each group. It's not very meaningful but it's also for illustrative purpose only.

If you only want to import one function or group of functions:
import { groupBy } from "lodash";

Related

React-TypeScript: This JSX tag's 'children' prop expects a single child of type 'ReactNode', but multiple children were provided

I am using react-typescript for a class project in APIs, and am having issues. I am trying to use .map to loop through an array of "popular" movies, but am getting the error message "This JSX tag's 'children' prop expects a single child of type 'ReactNode', but multiple children were provided." I have a single div tag as the "parent", so I don't understand why it is giving me this error. When I take the loop out and just put regular elements, I don't get the error message. Sorry if I'm not wording it too great, I'm still fairly new to this.
Here is my Popular component
import React, { useEffect, useState } from 'react';
import Genres from '../models/Genres';
import Results from '../models/Popular';
import GenreListService from '../services/GenreList';
import getPopular from '../services/GetPopular';
export default function Popular() {
const[popular, setPopular] = useState<Results[]>()
const [genres, setGenres] = useState<Genres[]>()
useEffect(() => {
getPopular().then(data => {
setPopular(data);
});
}, []);
useEffect(() => {
GenreListService().then(data => {
setGenres(data);
});
}, []);
return (
<div className="Popular"> {/* This is where I get the error message */}
<h1>POPULAR MOVIES</h1>
{
popular?.map((thing, index) => {
<div key={index}>
<p>{thing.title}</p>
<p>{thing.overview}</p>
<p>{thing.genre_ids}</p>
</div>
})
}
</div>
)
}
Here is my Popular interface
export interface Popular {
results: Results[]
}
export default interface Results {
backdrop_path: string,
genre_ids: number[],
id: number,
overview: string,
poster_path: string,
release_date: string,
title: string,
vote_average: 6.8,
}
And here is the API I got it from.
import axios from "axios";
import Results, { Popular } from "../models/Popular";
export default function getPopular(): Promise<Results[]>{
return axios
.get<Popular>("https://api.themoviedb.org/3/movie/popular?api_key={api-key}")
.then((response) => {return response.data.results})
}
ANY input would be greatly appreciated!!
Update: I figured it out. I needed to get rid of the curly brackets inside of the .map loop
To complement the answer.
The issue was that your .map iterated with a void function (a function that doesn't return anything).
You had 2 options:
Adding a return;
Removing brackets.
When working with Arrow Functions, you can write a single-line function that returns it's only statament.
This:
[1, 2].map((integer) => {
return integer + 1
})
It the same as this:
[1, 2].map((integer) => integer + 1)
Both return integer + 1, but only the first must to explicit call for return, or else nothing is returned.
By removing return, you end up with an array of undefined.
Here is a snippet to help with visualizing it:
result1 = [1, 2].map((integer) => {
return integer + 1
})
console.log(result1) // > [2, 3]
result2 = [1, 2].map((integer) => integer + 1)
console.log(result2) // > [2, 3]
result3 = [1, 2].map((integer) => {
integer + 1
})
console.log(result3) // > [undefined, undefined]

Passing data to lodash map() function - functional components

This is example of my products.json
{
"products": [
{
"product_id": 1,
"product_name": "M-Z-8Nm",
"supplier_id": 1,
"product_cat": "Motori",
"product_subcat": "Zglobni motori",
"product_char": "8Nm",
"product_uom": "kom",
"product_quantity": "20",
"product_commentar": ""
},
{
"product_id": 2,
"product_name": "M-P-10Nm",
"supplier_id": 1,
"product_cat": "Motori",
"product_subcat": "Pomoćni motori",
"product_char": "10Nm",
"product_uom": "kom",
"product_quantity": "13",
"product_commentar": ""
}
]
}
Now, in component under i am mapping options for Select field from products.json ( react-select ) and return it. In this example i am mapping product-name as option for select button.
I would like to make this component reusable so i could pass data with props and use it (ie. product-id instead of product-name. Data from props are stored in const extractProps which is typeof String (but dont need to be).
I have problem replacing key from products product_name with data from props extractProps.
ReactSelectComponent.js:
import React from "react";
import Select from "react-select";
import FetchDataCustomHook from "./FetchDataCustomHook.js";
import _ from "lodash";
const ReactSelectComponent = (props) => {
// extractProps is typeof string and need to replace "product_name"
const extractProps = props.propsFromForm
const options = _.map(
FetchDataCustomHook(),
function (products) {
return {label: products.product_name, value: products.product_name}
});
return (<Select options={options}/>)
}
export default ReactSelectComponent;
You don't really need lodash to accomplish that map, this is a solution using pure js:
const ReactSelectComponent = ({property}) => {
const options = products.map((product) => {
return { value: product[property], label: product[property] }
});
return (<Select options={options}/>);
}
If you want to use lodash then it the options would be like this:
const options = _.map(products, (product) => {
return { value: product[property], label: product[property] }
})
And this is how you called the component <ReactSelectComponent property='product_name' />.
I stored the json you posted as the products variable.

Vuex abstract common methods for inheritance (Large SPA)

Large SPA Vuex question - is there a way to abstract common methods that can be inherited or injected into Vuex namespace modules? What I have below works, but feels kludgy as I have to standardize states, etc. I could pass in assoc. array keys to accommodate different state stores, but looking to streamline. Ive look at Vuex plugins and I think there is way to use that to some degree, but again not ideal if I wanted something as simple as getById()
Another approach is to create a Vue IoC factory container, Vue provider, with a Vuex driver, services for for common components, but that is a lot of overhead and I feel would be an overkill, but maybe that is the best approach for a large SPA. Would appreciate some guidance as to where this is the right approach.
vuex-common.js
Collection of common service methods. I could create a common for getters, mutations, actions, etc.
import isArray from 'lodash/isArray'
export function getById () {
return state => {
const find = val => state.data.find(x => x.id === val)
return val => isArray(val) ? val.map(find) : find(val)
}
}
Namespace store example -- store/modules/Users.js
// removed other imports for brevity sake
import { getById } from 'vuex-common'
import forEach from 'lodash/forEach'
import {fetchUsers} from '~/api/apis/users.api';
const initialState = () => ({
data: []
});
const state = initialState();
const getters = {
getById: getById() // vuex-common
// removed other getters for brevity sake
}
const actions = {
async getUsers({commit, getters}) {
try {
const {data} = await fetchUsers();
forEach(data, (u) => {
/* Import getById vuex-common function */
if (!getters.getById(u.id)) {
commit('ADD_USER', u);
}
});
} catch (e) {
console.error("ERROR", e.message, e.name);
}
},
}
// removed mutations and exports for brevity sake

React Native Arrow Syntax Explanation

I am reviewing some code and was unable to find a breakdown of this arrow function syntax. Could someone help explain what the parameters ({ match, onOpen }: MatchListItemProps) mean?
import React from 'react';
import { ListItem } from 'react-native-elements';
import { StyleSheet } from 'react-native';
type MatchListItemProps = {
match: User,
onOpen: Function
};
const styles = StyleSheet.create({});
const TestScreen = ({ match, onOpen }: MatchListItemProps) => {
const { name, image, message } = match;
return (....
Could someone help explain what the parameters ({ match, onOpen }: MatchListItemProps)mean?
This code is using typescript and destructuring. Let me get rid of both for a second, then add them back in. Here it is in pure javascript without destructuring:
const TestScreen = (props) => {
let match = props.match;
let onOpen = props.onOpen;
Now i'll add back in the typescript. A variable can be followed by a colon and then a type. This information is used to catch type errors at compile time.
const TestScreen = (props: MatchListItemProps) => {
let match = props.match;
let onOpen = props.onOpen;
And then adding in the destructuring. This is a shorthand to pluck values off an object and assign them to local variables:
const TestScreen = ({ match, onOpen }: MatchListItemProps) => {
Since the parameter is an object, you can deconstruct it inside the parameter.
For instance, take a look at this code
let person = {
name: 'Felipe',
age: '23'
}
You could take the values in this form
let name = person.name
let age = person.age
Or you could use a shortcut with destructuring assignment
let { name, age } = person
Finally, if the variable person inside a parameter, you can deconstruct it inside the very parameter
logPersonNameAndAge = ({ name, age }) => {
console.log(name)
console.log(age)
}
So that you could call it passing the entire object
logPersonNameAndAge(person)
Your code is TypeScript, not just JavaScript. : MatchListItemProps is a type annotation that's used by TypeScript, which is used to catch many common errors at compile time instead of at runtime. ({ match, onOpen }) => { ... is a destructuring, which means the function takes an object, and brings into scope variables called match and onOpen containing whatever was in the object with those names. It's roughly equivalent to obj => { let match = obj.match, onOpen = obj.onOpen; .... In turn, const TestScreen = obj => { ... is a lambda, which is roughly equivalent to function TestScreen(obj) { ....

Jasmine spy on RxJS 5.5 operators

I am trying to spy on RxJS operators with Jasmine. There are different use cases in my tests where I want to be in control on what a Observable returns. To illustrate what I am trying to do I have created the example above even thought it does not make to much sense as this observable always returns the same hard coded string. Anyway it is a good example to show what I am trying to achieve:
Imagine I have the following Class.
import {Observable} from 'rxjs/Observable';
import {of} from 'rxjs/observable/of';
export class AwesomeTest {
constructor() {
}
getHero(): Observable<string> {
return of('Spiderman');
}
}
And the following test:
import {AwesomeTest} from './awesomTest';
import {of} from 'rxjs/observable/of';
import createSpyObj = jasmine.createSpyObj;
import createSpy = jasmine.createSpy;
describe('Awesome Test', () => {
let sut;
beforeEach(() => {
sut = new AwesomeTest()
})
fit('must be true', () => {
// given
const expectedHero = 'Superman'
const asserter = {
next: hero => expect(hero).toBe(expectedHero),
error: () => fail()
}
createSpy(of).and.returnValue(of('Superman'))
// when
const hero$ = sut.getHero()
// then
hero$.subscribe(asserter)
});
});
I try to spy on the Observable of operator and return a Observable with a value that I specified inside my test instead of the actual value it will return. How can I achieve this?
Before the new Rx Import Syntax I was able to do something like this:
spyOn(Observable.prototype,'switchMap').and.returnValue(Observable.of(message))
In your spec file, everything as a wildcard (don't worry about tree shaking, this is just for the tests)
import * as rxjs from 'rxjs';
You can then use rxjs for your spying
spyOn(rxjs, 'switchMap').and.returnValue(rxjs.of(message))

Categories