React: Handling an onChange within mapping render - javascript

I have created a matrix/table using material ui and have also used a button toggle to select a value.
The table is a matrix where a member can see what their current membership is, and they can choose from other membership types based on age... seniors, juniors, infants.
https://codesandbox.io/s/64j65yrxpw
The demo above will give more clarity. The issue is that when I select an option of yes, no or maybe - all options across all people are selected rather than just that person in that row and across all membership types - (click an option and you will see the problem).
The below is what feeds the table:
const selected = [
{ personId: "0001657", fullName: "Joe Bloggs", seniorStatus: "Yes" },
{ personId: "0001666", fullName: "John Doe", seniorStatus: "No" }
];
and ideally will want something like this returned based on selections:
const newlySelected = [
{ personId: "0001657", fullName: "Joe Bloggs", seniorStatus: "Yes", juniors: "maybe" },
{ personId: "0001666", fullName: "John Doe", seniorStatus: "No", juniors: "no", infants: "yes" },
];
I appreciate that there are two issues here... the toggle not behaving individually and the return of desired json - any help would be awesome. thanks

You have a "too simple" state.
state = {
alignment: "left",
formats: ["bold"]
};
Your state.alignment used by all buttons. If you want to keep it in the form of single component you need to make it 2D array:
[
['<#1 btn value>', '<#2 btn value', ...],
[..., ..., ...]
]
Then when you call setState you need to change the relevant item in the 2D array based on the action location. You have to indexes, one of the row, one of the cell, you can use them.
UPDATE
New state - 2D array
value: [
["no", "no", "no"],
["no", "no", "no"]
]
New ToggleButtonGroup - using rowIdx and cellIdx to have the right position in the 2D array
<ToggleButtonGroup
className={classes.toggleButtonGroup}
value={value[rowIdx][cellIdx]}
exclusive
onChange={(event, val) => this.handleValue(event, val, rowIdx, cellIdx)}
>
New handleValue - using the rowIdx, cellIdx to change the value in the right position.
handleValue = (event, val, rowIdx, cellIdx) => {
const newValue = [...this.state.value]
newValue[rowIdx][cellIdx] = val
this.setState({
value: newValue,
})
}

Related

React Cascading Dropdowns (Dynamic)

The scenario is as follows: I want to make a reusable cascading dropdown component, however every video/article that I've seen on the topic just uses hard coded dependent dropdowns such as: Country => State => City.
However, for my situation it will not always be the same dependencies. How can I support custom dependency for cascading dropdowns?
For a hardcoded example I would do something along the lines of having one useEffect for each of the options, and make the dependant options change when the parent changes.
I have an object example to iterate through of one page I am trying to accomplish this for:
[
{
key: 0,
name: "State",
parentQuestion: null,
inputType: "Dropdown",
},
{
key: 1,
name: "Sublocation",
parentQuestion: "State",
inputType: "Dropdown",
},
{
key: 2,
name: "Operation",
parentQuestion: "Sublocation",
inputType: "Dropdown",
},
{
key: 3,
name: "Installment Number",
parentQuestion: "Operation",
inputType: "Dropdown",
},
{
key: 4,
name: "Upload Directory",
parentQuestion: null,
inputType: "File",
},
{
key: 5,
name: "Download Directory",
parentQuestion: null,
inputType: "Directory",
},
]
Is it possible to accomplish this? Or must I hardcode the logic with different hooks for each page?
You can have the component built passing the array data as props, then iterating over each key on an element of the array, you have to build the selects using map. For the option values list you have to prepare another object such that
optionList={country:[],state:[],city:[]}
You have to maintain an object with selected options, and onchange of the selects you have to modify the object for selected options.
Edit
Also onchange of selects you have to modify the optionList object, reducing the options array, for example
if country is selected 'UK' then on the final object selected you add the value final_object={country:'UK'} and then in optionList you have to reduce the options for corresponding state and city

How to merge arrays that derive from the same variable

I have the following line of code which returns me one or more arrays depending on the checkbox that is clicked.
selected.forEach(langsel => {
let filtered = person.filter(pers => pers.language == langsel);
})
selected and I do not report the other variables for simplicity in reading.
I have a list of checkboxes where each refers to a particular language. So every time a checkbox is clicked I want the people who speak that language to be returned to me; this must be returned in an array of objects.
catsel refers to the checkbox that is selected (being a checkbox, more than one can also be selected). So for each language that is selected it returns me the array of objects in the filtered variable.
For example, if I select the English language through the checkbox, I get:
[{id: "2", name: "Tomas Addreh", language: "English"},{id: "6", name: "Mark Addreh", language: "English"}];
if together with the checkbox selected previously, therefore English, I also select the checkbox relating to the Spanish language, filtered returns me:
[{id: "2", name: "Tomas Addreh", language: "English"},{id: "6", name: "Mark Addreh", language: "English"}];
[{id: "15", name: "Alex Atres", language: "Spanish"}, {id: "1", name: "Mark Sertoj", language: "Spanish"}, id: "12", name: "Martha Forrest", language: "Spanish"];
filtered in the latter case returns me two separate arrays.
I want them to be merged into an array.
Can anyone kindly help me?
I concat the array by using spread operator.
let people = [];
selected.forEach(langsel => {
let filteredPerson = person.filter(pers => pers.language == langsel);
people = [...people, ...filteredPerson];
})

Searching from multiple keys inside object in javascript (reactJS)

i have the the following array of object which indicated all the possible combination of a product
[{id:25,attributes:[{id: 0, name: "Weight", option: "250gr"},{id: 0, name: "Roast", option: "Medium"},{id: 0, name: "Packaging", option: "Card"}
{id:26,attributes:[{id: 0, name: "Weight", option: "500gr"},{id: 0, name: "Roast", option: "Medium"},{id: 0, name: "Packaging", option: "Card"}
{id:27,attributes:[{id: 0, name: "Weight", option: "250gr"},{id: 0, name: "Roast", option: "Light"},{id: 0, name: "Packaging", option: "Card"}
{id:28,attributes:[{id: 0, name: "Weight", option: "250gr"},{id: 0, name: "Roast", option: "Light"},{id: 0, name: "Packaging", option: "Tin"}
]
On the webpage, options are set via select; the select also set a state component as follow:
{Weight:"",Roast:"",Pakaging:""}
with the values from the selected option, for example (assuming only weight and roast are selected):
{Weight:"250gr",Roast:"Light",Packaging:""}
everytime a option is selected i want to narrow down the combination of product (based on the selection) until i finally got only one option available (also the combination are used for dinamically repopulate select, adding or removing available options based on previous selections)
until now i was using something like (inside the function called by onChange on a select):
const result = variations.filter((p) =>
p.attributes.some((c) => c.option === event.target.value)
);
but this way it reduces based on the latest selection, so if i start selecting 250gr it gives me a set of 3 (25,27,28)
then if i select Medium, it give me a set of 2 (25 and 26) filtering only on medium and ignoring Weight
i'm looking to convert my filter, instead that on event.target.value, on the object but i cant find the right method, anyone can help?
You need to persist the selected filters like this:
const [filters, setFitlers] = React.useState({});
const onFilterChange = (e) => {
setFitlers(prevFilters => ({...prevFilters, [e.target.name]: e.target.name})
}
Now you can filter like this:
const result = variations.filter((p) =>
Object.entries(filters).every(([key, value]) => p.attributes[key] === value)
);
Obejct.entries will give you the key and value pairs of the filter object. And with every, you check if all the filters match.
Now you only have to give the input a name to fit your name:
<input name="weight" onChange={onFilterChange} .../>
To remove filters, maybe filter the filter object as well to remove empty strings:
Object.entries(filters).filter(([key,value) => value).every....

How to change the location of an object key value pair in JavaScript

I've seen similar questions to this one but in different languages and I am struggling to create a JavaScript equivalent.
I am receiving an object and through a function I want to change the location of one (or more) of the properties. For example,
With the original object of
{
individual: [
{
dob: '2017-01-01',
isAuthorized: true,
},
],
business: [
{
taxId: '123',
},
],
product: {
code: '123',
},
}
I would like to change the location of isAuthorized to be in the first object inside of the business array instead of individual.
Like so
{
individual: [
{
dob: '2017-01-01',
},
],
business: [
{
taxId: '123',
isAuthorized: true,
},
],
product: {
code: '123',
},
}
So far I was trying to create an object that would contain the key name and location to change it to, e.g.
{
isAuthorized: obj.business[0]
}
And then loop over the original object as well as the object with the location values and then set the location of that key value pair.
Basically, in this function I want to see that if the original object contains a certain value (in this case isAuthorized) that it will take that key value pair and move it to the desired location.
What you want can easily be achieved by using loadsh, here's a working snippet of how to restructure based on defined structure map. Extended this example to match what you want.
The example is doing a deep clone, if you are fine modifying the original object then skip that step to avoid the overhead.
// input data
const data = {
individual: [
{
dob: '2017-01-01',
isAuthorized: true,
},
],
business: [
{
taxId: '123',
},
],
product: {
code: '123',
},
};
// the structure change map
const keyMap = {
'individual[0].isAuthorized': 'business[0].isAuthorized'
};
function parseData(data,keyMap) {
const newData = _.cloneDeep(data);
for( let [source,dest] of Object.entries(keyMap) ) {
_.set(newData,dest,_.get(newData,source));
_.unset(newData,source);
}
return newData;
}
console.log(parseData(data, keyMap));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
Note: loadsh's set consider any numeric value as an array index so if you are using a numeric object key then use loadash.setWith. I recommend reading examples in doc for a better understanding.
https://lodash.com/docs/4.17.15#set

Javascript Map a Collection

The Issue:
I'm attempting to build a simple search tool. It returns a search query by matching an id to another item with the same id. Without going into the complexities, the issue I'm having is that when my data was organized previously, the map function from javascript returned all the results perfectly. However, now that my data is structured a bit differently (a collection, I think?) ....the ids don't appear to be lining up which causes the wrong search results to show.
The function in question:
const options = this.props.itemIds.map((id) => (
<Option key={this.props.itemSearchList[id].id}>
{this.props.itemSearchList[id].name}
</Option>
));
When the data was structured like this it worked as expected:
Example of previous structure:
const items = [
{
id: 0,
name: "name 0",
tags: ['#sports', '#outdoor', '#clothing'],
},
{
id: 1,
name: "name 1",
tags: ['#sports', '#outdoor', '#clothing'],
},
{
id: 2,
name: "Name 2",
tags: ['#sports', '#outdoor', '#clothing'],
},
Now that the data is a ?collection...the map function doesn't work as anticipated and it returns improper results or none at all: I've been able to use the lodash Map function on this structure successfully in the past.
Here's a screenshot of the new data:
I believe a representative way to write out the example would be:
const newItems = [
0: {
id: 0,
name: "name here",
},
1: {
id: 1,
name: "name here",
},
]
Any recommendations for making this work or need more info? Perhaps I'm misunderstanding the issue entirely, but I believe it has to do with data structure and the map function from JS. I can see results returning, but the id's are not lining up appropriately anymore.
Here's a visual representation of the misalignment. The orange is the search input and it pulling the right result. The green is the misalignment of what it's actually showing because of the data structure and mapping (I assume).
The issue is you were using index and lining that up with id as a sort of pseudo-key which is...beyond fragile. What you should be doing is keying by id (meaing itemsshould be an object) and then having a seperate array that stores the order you want. So items would be an object keyed by id:
const items = {
1: {
id: 1,
name: "name 1",
tags: ['#sports', '#outdoor', '#clothing'],
},
2: {
id: 2,
name: "name 2",
tags: ['#sports', '#outdoor', '#clothing'],
},
9: {
id: 9,
name: "Name 9",
tags: ['#sports', '#outdoor', '#clothing'],
},
};
And then itemIds (which it appears you already have) is an array with the correct order:
const itemIds = [1,9,2];
And then they can be accessed in the right order by looping over that array, and getting the element by said key:
itemIds.map((id) => {
const item = items[id];
// do something with the item
}
Take a look at how Redux recommends normalizing state shape.
https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape
What you call "collections" and "maps" are actually arrays. Now one of the arrays has the objects exactly at the position in the array that matches the id:
items[5].id === 5
Now through sorting /mutating / whatever you change the order so that the element at a certain position doesnt have that as an id:
newItems[5].id // 7 :(
That means that you cannot access the item that easy anymore, you now either have to sort the array again to bring it into order, or you search for an object with the id:
newItems.find(item => item.id === 5) // { id: 5, ... }
Or you switch over to some unsorted collections like a real Map:
const itemsMap = new Map(newItems.map(item => ([item.id, item])));
So you can get a certain item with its id as:
itemsMap.get(5) // { id: 5, ... }
... but the whole thing doesnt have to do with Array.prototype.map at all.
Here was my simple solution:
const options = [];
this.props.itemList.forEach((item) => {
if (this.props.searchResults.includes(item.id)) {
options.push(<Option key={item.id}>{item.name}</Option>);
}
});
Let me know what you think (to the group that tried to help!)

Categories

Resources