How to Resolve Encountered two children with the same key React.js - reactjs

Been having trouble getting this 'Encountered two children with the same key' error sorted. I've gone through a few threads regarding this issue and none of the answers seem to work in my situation. I want to avoid using index as well as its discouraged.
useEffect(() => {
const Items = locations.map((location) => {
return {
header: location.description,
key: location.code,
content: location.phoneNumber,
image: {
as: Avatar,
},
code: location.code,
};
});

useEffect(() => {
const Items = locations.map((location) => {
return {
header: location.description,
key: location.code,
content: location.phoneNumber,
image: {
as: Avatar,
},
code: location.code,
};
});
The key location.code is not unique and is used multiple times, you need unique keys. A fix could be this :
useEffect(() => {
const Items = locations.map((location,index) => {
return {
header: location.description,
key: index,
content: location.phoneNumber,
image: {
as: Avatar,
},
code: location.code,
};
});
But the best would be something like a unique id. Maybe location.id if thats available..

Is there a unique id in the location object? in which case, that is the perfect solution. Using the index is discouraged, but could potentially be combined with your location.code and concatenated.
useEffect(() => {
const Items = locations.map((location,index) => {
return {
header: location.description,
key: index + location.code,
content: location.phoneNumber,
image: {
as: Avatar,
},
code: location.code,
};
});

Related

Create and update inside map function

I'm trying to find the right way to create and consequently update inside a map function.
These are the steps I need:
Map function "reads" the array of elements ids
Create new record on "leads_status" table
Using the new record id (from "leads_status") "leads" table is updated using "leads_status.id" as foreign key related to "leads.id_ls"
This is the code I tried.
const [create, { isLoading: isLoadingCreate, error: errorCreate }] = useCreate();
const [record, setRecord] = React.useState(null);
leadsIDS.map((value, index) => {
create('leads_status', {
data: {
id_lead: value,
id_status: 5
}
}, {
onSuccess: ({ id }) => {
setRecord([id, value]);
},
onError: () => {
console.log();
}
});
update('leads', {
id: record[1],
data: {
id_ls: record[0]
}
}, {
enabled: !isLoadingCreate && record !== null
}, {
onSuccess: () => {
console.log(record);
},
onError: error => notify('Error', { type: 'warning' })
})
})
I tried also to put the "update" function inside the "create --> onSuccess" but also there the code is not working as I want.
In "leads_status" table records are always created for each element in "leadsIDS" array but in "leads" table only 1 records is updating.
Where am I wrong?
The useCreate and useUpdate hooks are designed for single actions. If you want to chain several actions, I suggest you use the useDataProvider hook, instead, which lets you manipulate Promises.
const dataProvider = useDataProvider();
const notify = useNotify();
try {
await Promise.all(leadsIDS.map(async (value, index) => {
const { data: leadStatus } = await dataProvider.create('leads_status', {
data: {
id_lead: value,
id_status: 5
}
});
await dataProvider.update('leads', {
id: value,
data: { id_ls: leadStatus.id }
});
}));
} catch (e) {
notify('Error', { type: 'warning' });
}

Update an array relation belongs to many with Strapi controller

I use Strapi V4. I have a link collection and I want to update likes.
How update the relation array ? When I put new data old value are replace by the new one.
Example :
likes : [1]
if I update another time
likes:[2].
BUT I want this likes : [1,2]
I try this but It d'oesn't work. Thans for your replay
'use strict';
/**
* link controller
*/
const { createCoreController } = require('#strapi/strapi').factories;
module.exports = createCoreController('api::link.link', ({ strapi }) => ({
// Method 2: Wrapping a core action (leaves core logic in place)
async find(ctx) {
const { data, meta } = await super.find(ctx);
const linkId = data.map((link) => link.id);
const allPosts = await strapi.entityService.findMany('api::link.link', {
fields: ["id"],
filters: { id: { $in: linkId } },
populate: {
likes: { count: true },
},
});
data.forEach(link => {
link.likes = allPosts.find(({ id }) => id === link.id)?.likes?.count || 0;
});
//update value with new array => need to be fix
await strapi.entityService.update("api::link.link", {
likes: [...allPosts.likes.map(({ id }) => id), ...likes],
});
return { data, meta };
},
}));
This part need to be fix. Can you help me ? Thanks
//update value with new array => need to be fix
await strapi.entityService.update("api::link.link", {
likes: [...allPosts.likes.map(({ id }) => id), ...likes],
});

ReactJS: axios POST with recursive function

CreateFolder.js
Hi!! I'm trying loop an js tree object to create a folder for the childs elements with de Egnyte API.
I think I have posed the problem wrong.
My recursive funcion is cheking if the parent have childs, if this true, loop his childs, if the child isn't a parent, i want to create the folder with this child id.
The problem is when I see the console, first there are all prints of the '-----ADD-----', after that, there are all prints of the '-----ADDED' so I don't understant why they aren't simultaneously
The API works fine, this create my 6 folders, the problem of this code is when I loop my original TREE that have 400 childs, and I need to create this 400 folders but the callbacks doesn't work, this creates about 60 folders, and there are much 403 errors. I think there are much calls in a short time.
I tried to call the function addFolder with a time out or doing the function async but doesn't work too.
I explained my problem well? Can someone help me?
Thank you so much!!
import { ConnectingAirportsOutlined } from "#mui/icons-material"
import axios from "axios"
import { useEffect, useState } from "react"
import { useSelector } from "react-redux"
export default function CreateFolders() {
console.log('======CreateFolders=====')
// const state = useSelector(state => state)
// const tree = state.assets.tree
const tree = [
{
id: '1000',
name: 'GRUPO GENERADORES',
child: [
{
id: '1100',
name: 'MOTORES',
child: [
{
id: '1100.1',
name: 'MOTOR 1'
},
{
id: '1100.2',
name: 'MOTOR 2'
},
{
id: '1100.3',
name: 'MOTOR 3'
},
]
},
{
id: '1200',
name: 'ALTERNADORES',
child: [
{
id: '1200.1',
name: 'ALTERNADOR 1'
},
{
id: '1200.2',
name: 'ALTERNADOR 2'
},
{
id: '1200.3',
name: 'ALTERNADOR 3'
}
]
}
]
}
]
useEffect(() => {
getTreeItems(tree)
}, [tree])
const api = 'https://test.egnyte.com/pubapi/v1/fs/Shared/test/'
const headers = {
"headers": {
"Authorization": "Bearer XXXXXXXXXX",
"Content_type": "application/json"
}
}
const body = {
"action": "add_folder"
}
const addFolder = async (id) => {
const endpoint = api + id
await axios.post(endpoint, body, headers).then(res => {
console.log('OK')
return
}).catch((error) => {
console.log('ERROR')
})
}
const getTreeItems = treeItems => {
return treeItems.map(item => {
if (item.child && item.child.length > 0) {
getTreeItems(item.child)
}
if (item.id.includes('.')) {
console.log('-------ADD-------')
console.log(item.id)
addFolder(item.id)
console.log('-------ADDED')
}
})
}
return (
<>
<h2>CreateFolders</h2>
</>
)
}
The description you provided seems like you reached the rate limit (per second/daily).
Try to explore the response headers in failed requests to get more information about:
What kind of quotas have you exceeded (per second/daily)
When you can retry the request (e.g Retry-After header)
There are 2 solutions you can try:
Use bulk operation (create all the entities by single request) if the API supports it. This is a preferable solution
Retry the request after the timeout you get from the failed request header (e.g Retry-After header)
I solved my problem with the setTimeout with an index, because my function was executing the whole thing after 3 seconds, and with this index, add more time to every item, and executes each item 3 seconds after the previous one
My function after:
folders.map((id) => {
setTimeout(() => {
addFolder(id)
}, 3000)
})
My function before:
folders.map((id, index) => {
setTimeout(() => {
addFolder(id)
}, 3000 * index)
})
It's silly but I didn't know how the setTimeout worked

Gutenberg Block Variation Picker not working

I'm trying to add the BlockVariationPicker like in the WordPress Github example:
import { useSelect } from '#wordpress/data';
import {
__experimentalBlockVariationPicker as BlockVariationPicker,
store as blockEditorStore,
} from '#wordpress/block-editor';
const MyBlockVariationPicker = ( { blockName } ) => {
const variations = useSelect(
( select ) => {
const { getBlockVariations } = select( blocksStore );
return getBlockVariations( blockName, 'block' );
},
[ blockName ]
);
return <BlockVariationPicker variations={ variations } />;
};
In my edit function I'm adding:
{ MyBlockVariationPicker }
The block variation picker does not show.
I have already registered my bloc variations with scope block:
registerBlockVariation(
'my/testimonial',
[
{
name: 'testimonial-1',
title: 'Testimonial 1',
scope: ['block'],
attributes: {
example: 'testimonial-1'
},
},
{
name: 'testimonial-2',
title: 'Testimonial 2',
scope: ['block'],
attributes: {
example: 'testimonial-2'
},
}
]
);
The block variations should show in { MyBlockVariationPicker } but the don't. Unfortunately there isn't much documentation about this. How can we render the variations of a block using the Block Variation Picker as shown in the Github example?
Both the Columns and Query block use __experimentalBlockVariationPicker and its a really nice component/UI and I agree, it there aren't many examples of how to use it, most likely as its still 'experimental' and still likely to change.
I found that both the Columns and Query blocks display the BlockVariationPicker by checking if the current block (by clientId) contains any InnerBlocks; if there are none, the BlockVariationPicker is shown. When using this component in your own block, you will need some attribute or property to check whether or not a variation has been selected.
I've put together a basic/working example using the structure of your my/testimonial block + variations and based on how the BlockVariationPicker is implemented in Columns block:
import { get } from 'lodash';
import { useSelect } from '#wordpress/data';
import { registerBlockType, registerBlockVariation, store as blocksStore } from '#wordpress/blocks';
import { useBlockProps, __experimentalBlockVariationPicker as BlockVariationPicker } from '#wordpress/block-editor';
// Create our own BlockVariationPicker
const MyBlockVariationPicker = ({ name, setAttributes }) => { // Note: We need "name" and "setAttributes" from edit() props
const { blockType, variations, defaultVariation } = useSelect(
(select) => {
const { getBlockVariations, getBlockType, getDefaultBlockVariation } = select(blocksStore);
return {
blockType: getBlockType(name),
defaultVariation: getDefaultBlockVariation(name, 'block'),
variations: getBlockVariations(name, 'block')
};
},
[name]
);
return <BlockVariationPicker
variations={variations}
icon={get(blockType, ['icon', 'src'])}
label={get(blockType, ['title'])}
onSelect={(nextVariation = defaultVariation) => {
if (nextVariation.attributes) {
setAttributes(nextVariation.attributes); // Use setAttributes to set the selected variation attributes
}
}}
/>;
};
// Register the Block Variations
registerBlockVariation(
'my/testimonial',
[
{
name: 'testimonial-1',
title: 'Testimonial 1',
icon: 'admin-comments', // Added icon so the variation is visibly different (optional)
scope: ['block'],
attributes: {
example: 'testimonial-1'
},
isDefault: true
},
{
name: 'testimonial-2',
title: 'Testimonial 2',
icon: 'admin-links',
scope: ['block'],
attributes: {
example: 'testimonial-2'
},
}
]
);
registerBlockType('my/testimonial', {
title: 'My Testimonial',
keywords: ['testimonial'],
icon: 'admin-post',
attributes: {
example: {
type: "string", // no default set, example is "undefined"
}
},
edit(props) {
const { attributes, setAttributes } = props;
// If example is undefined, show Variation Picker
if (attributes.example === undefined) {
return (
<MyBlockVariationPicker {...props} />
);
}
// Otherwise show the Editor
return (<div {...useBlockProps()}><h2>{attributes.example}</h2></div>);
},
save: ({ attributes }) => {
return <div {...useBlockProps.save()}><h2>{attributes.example}</h2></div>;
}
})
If you build the above javascript, the resulting block allows you to pick from the two variations on insertion:

How to get items so that it is not null (useEffect)?

How to get items so that it is not null? I'm trying to write the values ​​that I get through useEffect to sidebat -> items, but I get null, in general, is it mandatory to use 2 useEffect? Maybe you can somehow do it through one, so as not to duplicate the code?
const [sidebarItemsLeagues, setSidebarItemsLeagues] = useState(null);
const [sidebarItemsCountries, setSidebarItemsCountries] = useState(null);
const [sidebars] = useState({
leagues: {
title: "TITLE 1",
items: sidebarItemsLeagues // null...
},
countries: {
title: "TITLE 2",
items: sidebarItemsCountries // null...
}
});
useEffect(() => {
api.getTestLeagues()
.then(data => setSidebarItemsLeagues(data)); // for items 1 (not null)
}, []);
useEffect(() => {
api.getTestCountries()
.then(data => setSidebarItemsCountries(data)); // for items 2 (not null)
}, [])
If sidebars is a computed value, then don't create a state variable for it. By using useState you specify only what it's initial value is, and then any updates need to be done by calling setSidebars, which you never do.
const sidebars = {
leagues: {
title: "TITLE 1",
items: sidebarItemsLeagues
},
countries: {
title: "TITLE 2",
items: sidebarItemsCountries
}
}
2 useEffect is not mandatory, you can do both fetches one by one ... but you probably want to run them in parralel, not sequentially.
You can block rendering with empty data by
<>
{sidebarItemsLeagues && sidebarItemsCountries && (
<LeaguesList data={sidebarItemsLeagues} />
<Countries data={sidebarItemsCountries} />
)}
</>

Resources