Passing data to lodash map() function - functional components - javascript

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.

Related

Problem updating a list with redux toolkit

// './file_1'
import { createSlice } from '#reduxjs/toolkit';
export const array = createSlice({
name: 'array',
initialState: {
value: []
},
reducers:{
append(state, a) {
state.value.push(a)
}
}
});
export const { append } = array.actions;
export default array.reducer;
I've also updated the store, so no problem on that side
'/.file_2'
import { useSelector, useDispatch } from 'react-redux';
import { append } from './file_1';
const dispatch = useDispatch()
let x = 5
dispatch(append(x));
raises this error:
Objects are not valid as a React child (found: object with keys {type, payload}). If you meant to render a collection of children, use an array instead.
"a" is an object. The object looks like this -> {action: ..., payload: ...}
so you need to access it like this -> a.payload
Issue
The issue is that the code is attempting to render an object which isn't valid JSX.
Since you've no errors regarding arrays I'll assume you are correctly mapping it, something like this:
const array = useSelector(state => state.array);
...
return (
...
{array.map(el => (
<div>{el}</div> // <-- error, can't render object!!
))}
);
The error is error: Objects are not valid as a React child (found: object with keys {type, payload}) which is quite obvious the action object dispatched to the Redux store and saved into state.
export const array = createSlice({
name: 'array',
initialState: {
value: []
},
reducers:{
append(state, a) {
state.value.push(a) // <-- `a` is action object `{ type: 'append', payload: 5 }`
}
}
});
Solution
I'm sure you meant to save just the payload value and not the entire action object since the dispatched value x is a primitive number type.
const dispatch = useDispatch()
let x = 5
dispatch(append(x));
Update the reducer function to unpack the payload to save into state.
export const array = createSlice({
name: 'array',
initialState: {
value: []
},
reducers:{
append(state, action) {
state.value.push(action.payload); // 5
}
}
});

How to use right, reselect in react-redux aplication

I have an application where user can search data depending by his input. In my application i try to use reselect.
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { searchPersonAction } from "./store";
const Search = () => {
const dispatch = useDispatch();
const selector = useSelector((s) => s);
const search = (e) => {
const txt = e.target.value;
dispatch(searchPersonAction(txt));
};
return (
<div>
<input onChange={search} placeholder="search" />
<ul>
{selector.name.map((p) => (
<li key={p.name}>{p.name}</li>
))}
</ul>
</div>
);
};
export default Search;
In my store i have an array of persons like this:
export const persons = [
{
name:"jack",
age: 2
},
{
name:"Jim",
age: 14
},
{
name:"July",
age: 92
},
{
name:"Bill",
age: 1
},
{
name:"Carl",
age: 72
},
]
Now, when user search something, in the list appears the results according to the name which was searched by the user.
Question: Is the reselect usefull (protects from to many re-renders) in my case or not? Or using useSelector, in the case above is enought?
I don't think reslect here will be necessary. You can use useMemo to achieve the same result.
import React, { useEffect, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
import { searchPersonAction } from "./store";
const Search = () => {
const dispatch = useDispatch();
const persons = useSelector((s) => s.persons);
const [query, updateQuery] = useState('');
const searchedPersons = useMemo(() => persons.filter(p => p.name.includes(query)), [query]);
const search = (e) => {
updateQuery(e.target.value);
};
return (
<div>
<input onChange={search} placeholder="search" />
<ul>
{searchedPersons.map((p) => (
<li key={p.name}>{p.name}</li>
))}
</ul>
</div>
);
};
export default Search;
reselect will be useful if you get array or object from store for example:
store
state = {
persons: [
{
name:"Jack",
age: 2
},
{
name:"Jane",
age: 14
},
]
}
if you used selector from react-redux and if your typed 'J' in the search field and used searchPersonAction action, then it will change the array of persons in the store, but array stayed the same.
state = {
persons: [
{
name:"Jack",
age: 2
},
{
name:"Jane",
age: 14
},
]
}
then you receive rerender regardless of whether the data in the array has changed or not.
But if you use reselect and when your typed 'Ja' in the search field, it will be the same array, then you will not get a repeated render, because reselect will memoize data

Get items by name from JSON based on user input id react

Hey how would I go about getting items by name from a JSON file based on what a user inputs? I have a JSON file that has an id and name. I want the user to put in a number and then display the item name associated with that id number. I am using ReactJS for this. Any advice would be greatly appreciated. thank you
You can read JSON file as Javascript Object or Array.
I use react hooks. Sorry my English very bad.
import { useState } from 'react';
import jsonData from './data.json';
export default function Test() {
const [name, setName] = useState('');
const onChange = (e) => {
// jsonData is javascript array when import it
const data = jsonData.find((d) => d.id == e.target.value);
if (data) {
setName(data.name);
}
};
return (
<div>
<input onChange={onChange} />
<p>Name: {name}</p>
</div>
);
}
JSON file for test:
[
{
"id": 1,
"name": "abc"
},
{
"id": 2,
"name": "def"
},
{
"id": 3,
"name": "ghi"
}
]

How to test if component counts array length properly with Jest

I have a component that I am giving an array with objects as props to it like this:
describe('component', () => {
it('should return the correct number of items passed in the array', () => {
const comp = shallowMount(component, {propsData: {
buttons: [
{name:'button1'},
{name:'button2'}
]
}});
expect(component.length).toHaveLength(buttons).length
});
});
How can I test that the provided array has the correct length, for example if there are two objects in the array, the component should return two, if there is one, one, if there are none then it should return 0, how can I achieve that? I tried
expect(component.length).toHaveLength(buttons).length
But that does not work
I guess you want to check if the correct number of childs of some type was rendered (in Vue).
// import component that you want to count, e.g. Button
const buttons = [
{ name: 'button1' },
{ name: 'button2' }
]
const comp = shallowMount(component, { propsData: { buttons } })
expect(comp.findAll(Button).length).toBe(buttons.length)
https://lmiller1990.github.io/vue-testing-handbook/finding-elements-and-components.html#findall
const buttons = [
{name:'button1'},
{name:'button2'}
]
const comp = shallowMount(component, {propsData: { buttons }});
expect(comp.findAll(Button)).toHaveLength(buttons.length)

What is the right import for lodash constructor?

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";

Categories