Remove duplicate JSX elements | React Strapi graphql - reactjs

Long story short, i have and array of JSX elements, that looks something like this:
<ChatListCard
key={index}
prop1={prop1}
prop2={prop2}
prop3={prop3}
/>
I get the props for these "Cards" from two different tables in Strapi/Graphql API, so I do something like this:
[].concat(
array1.map((item,index)=> <Card key={index} prop1={item.prop1} ... />),
array2.map((item,index)=> <Card key={index} prop1={item.prop1} ... />)
)
The problem is that array1 and array2 contain some "items" that are identical, and need to be filtered out. Is there a way to do it, using JS:
[].concat(...).filter((magic)=> magic but filtered) //use the filter here
, or i should do it in GraphQL.
(I have already used where clause in there to filter out only the items that I do not need)
query ProposalsAndRequests($input:String!){
proposals(where: {_or:[
{owner:{email:$input}}
{task:{owner:{email:$input}}}
]},sort:"created_at:desc"){
id
...
}
}
chatRequests(where:{_or:[
{users_permissions_user:{email:$input}}
{task:{owner:{email:$input}}}
]},sort:"created_at:desc"){
id
...
}
}
note: chatRequests and Proposals contain identical fields, they just serve different purposes elsewhere in the site
the users_permissions_user and owner are also the same relation

You can do it using the "Set" datastructure in js. const set = new Set(arr);. Sets cant have duplicates! :-) but they can have identical objects if the references are not the same.
For a more complex filter, use .reduce function to accumilate only uniques.
Or, you could remove the duplicates by bruteforce it with something like:
const noDubs = [];
myArr.foreach(item => {
if(!noDubs.some(entry = entry.special.property === item.special.property) noDubs.push(item);
});

Related

is there a way to filter a product list without the page turning white?

I created a react product list using state and I also created a filter to filter the product.
My problem is when I clicked on the category button the second time my page disappear.
I tried not to use state to store data in memory but did not work. A link to my sandbox code is here.
https://codesandbox.io/s/l1b3od?file=/src/styles.css&resolutionWidth=612&resolutionHeight=675
You must change you filter function to this:
const Filtfunc = (prod)=>{
const result = Products.filter((x)=>{ // Here's the change
return x.category === prod
})
setData(result)
console.log(result)
}
You were filtering the data not the products, so once it's filtered the first time you have not all the products to filter, only the filtered data.
In your filter function you try to filter the Products by using data, but when you click to any category, you set the data to be that array with result which can be also empty. You have to filter the array by using Products, not the data you set later on. Thus, it should be:
const Filtfunc = (prod) => {
const result = Products.filter((x) => {
return x.category === prod;
});
setData(result);
};
You can also set if statement to check if your array returns anything or not:
<div className="create">
{data.length > 0 ? (
data.map((product) =>
// things here
)) : (
<h2>No result has been found</h2>
)}
</div>

Get access to an array in an array from REST API

Hi I'm trying to display variants in two separate categories Color and Size, to do this I need to get data from the api, I can access "attributes", but I would like to be able to access 0 and 1 and map them, I have no idea how to do this.
{variants.length > 1 ? (
variants.attributes.map(({ name, values }) => (
<ProductOptions
key={`key-${name}`}
name={name}
values={values}
selectedOptions={selectedOptions}
setOptions={setOptions}
/>
))
) : (
<Fragment />
)}
Thank you so much!
As i understand the output of Array with 6 elements where each of that 6 has attributes and attributes is yet another array and you want to loop through those attributes so you need 2 loops. One to loop through parent array and seconds inside the child.
variants.map((variant) => {
variant.attributes.map((attribute) => {
console.log('attribute: ', attribute);
console.log('attribute id: ',attribute.id);
});
});
p.s. you may use forEach https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach but it has little difference in this case.
Also it seems like you working with ReactJS and building some JSX to put into rendered jsx. I would argue to form the array upfront and in final jsx just insert the rows, so your final jsx will be more readable, especially in cases of double loops or any more complex jsx.
const attributes = [];
variants.map((variant) => {
variant.attributes.map((attribute) => {
console.log('attribute: ', attribute);
console.log('attribute id: ',attribute.id);
attributes.push(<div key={attribute.id}>{attribute.id}</div>)
});
});
// later
return (
<div>
{attributes}
</div>
)

Conditional classes on single instances of component within a loop

I have a loop of components that make a grid and I want some instances to have a background color change triggered by adding a class.
This question gave me ideas but I've yet to get them to work.
I have this basic markup in it.
<div className={`box ${(this.state.backgroundColor ? 'backgroundColor' : null)}`} key={i}>
</div>
And I have an array of the indexes representing the component instances that I want the color changed on
let indexes = [101, 178, 232, 545]
Currently I am doing it with plain JS, via document.querySelector('.box:nth-of-type(101'), but as this method bypasses React I want to change it.
Based on the other question I tried making an array of all the components and setting that to state, then looping through and using the indexes. I don't know how to "access" the component like this and get $$typeof: Symbol(react.element) etc
let compArr = []
this.arr.map((i) => {
compArr.push(
<div className={`box ${(this.state.backgroundColor ? 'backgroundColor' : null)}`} key={i}>
</div>
)
})
this.setState({
compArr: compArr
})
Then later loop over indexes:
indexes.map(index => {
this.state.compArr[index] ===> ??stuck here??
})
Expected Output: So on an array of three instances, say I want the second one only to have the class of backgroundColor:
<div className='box' </div>
<div className='box backgroundColor'</div>
<div className='box'</div>
Only in my case, I want the instances to correlate with the indexes array
How can I set the conditional class to true in those instances I have in the array? Say I have like 500+ instances total.
If I understand you correctly this is proably what you're looking for...
let indexes = [101, 178, 232, 545]
let compArr = this.arr.map((obj, i) => {
return (
<div
className={`box${( indexes.includes(i) ? " backgroundColor" : "")}`}
key={i}
>
</div>
);
})
this.setState({
compArr: compArr
})
If your goal is only to return the given div with the className backgroundColor for every element in this.arr with its index included in the indexes array then...
You only need to iterate over the array once and can perform all the necessary logic you're currently doing performing in two.
You don't need to use Array.push() to an outside collector when using any of the pure functions like map() or reduce() because they return a new version of whatever array is iterated over.
If for whatever reason you want a distinct array of <div>s with an index in indexes then you should use reduce...
let compArr = this.arr.reduce((acc, obj, i) => {
if (!indexes.includes(i)) return acc;
return [
...acc,
<div className="box backgroundColor" key={i}></div>
)]
},[])

How to calculate and store item property when rendering that item?

I need to calculate and store values from items, what been filtered earlier by some criteria and rendered:
<ItemsList>
items ?
items.map(
item =>
someCriteria &&
<ItemComponent details={item}>
)
<ItemsList />
I need something like let someVar += item.value somewhere to use it after map ends and before criteria changes.
I cant store it on-fly in local state, because of re-rendering while map runs
I see the only way to do this, for now, is to store it in localStorage, but it is even more stupid, I think.
Because of app architecture there is no redux store, only one state in main file.
Thanks
Though I don't quite understand what exactly you want to create. Here is something I think might be useful for you
var someVar = '';
const renderable =
<ItemsList>
{ items ?
items.map( item => {
if(someCriteria){
someVar += item.value;
return <ItemComponent details={item} key={a-must-key}>;
}
}
)
:
null
}
</ItemsList>
after the map end you can extract the value from the someVar

ReactJS - Listing all keys at once

I'm a beginner of ReactJS and I'm stuck trying to figure out why map only returns a single prop at a time.
In file1.jsx, I have an array that contains 3 objects:
var MainPanelOneData = [{"id":1,"shotviews":15080},{"id":2,"likes":12000},{"id":3,"comments":5100}];
File1.jsx also has a render function to extrapolate the data inside the array:
render: function() {
var ListMainPanelOne = MainPanelOneData.map(function(data) {
return <MainPanelOne key={data.key} shotviews={data.shotviews} likes={data.likes} comments={data.comments} />
});
In file2.jsx, I have this code to render the data object from file1.jsx:
render: function() {
return (
<div>
<span>{this.props.shotviews} shot views</span>
<span>{this.props.likes} likes</span>
<span>{this.props.comments} comments</span>
</div>
)
}
The result shows this:
15080 shot views likes comments
shot views12000 likes comments
shot views likes5100 comments
I'm guessing it repeats like this because it searches through one key at a time? If that's the case, how do I display all keys at the same time? Use indexing?
well your array of data doesnt have all the keys. each one of your objects in PanelOneData has ONE key and is missing the other two; additionally none of them have key called key. so youre making three MainPanelOne components, each with a single prop. the result of that map is this
[
<MainPanelOne key={null} shotviews={15080} likes={null} comments={null} />,
<MainPanelOne key={null} shotviews={null} likes={12000} comments={null} />,
<MainPanelOne key={null} shotviews={null} likes={null} comments={5100} />
]
which is an accurate display of what youre seeing
To get one line you might do something like this.
render: function() {
var ListMainPanelOne = MainPanelOneData.map(function(data) {
return <span key={data.id}> {data.shotviews} {data.likes} {data.comments} </span>
});

Resources