I'm taking an array and filtering the value in a context:
const { responsible } = useResponsible()
const [ids, setIds] = useState([])
const filteredResponsible = responsible?.filter((resp) =>
ids.includes(resp.id)
)
The problem is that I need to make a map to get the corresponding value of each id, one by one. This ends up making the code too long:
const { filteredResponsible } = useResponsible
const responsibleName = filteredResponsible.map((resp) => resp.name)
const responsibleEmail = filteredResponsible.map((resp) => resp.email)
const responsibleAddress = filteredResponsible.map((resp) => resp.address)
...
And so on with each item in the array.
I'm using the React Hook Form's setValue to set the value in the inputs:
useEffect(() => {
setValue('name', `${responsibleName}`)
setValue('email', `${responsibleEmail}`)
setValue('address', `${responsibleAddress}`)
setValue('cep', `${responsibleCep}`)
setValue('district', `${responsibleDistrict}`)
setValue('city', `${responsibleCity}`)
setValue('state', `${responsibleState}`)
setValue('phone', `${responsiblePhone}`)
setValue('sex', `${responsibleSex}`)
}, [])
How can I make these maps smaller? Without having to make a map to get each item in the array?
There doesn't seem to be any reason to do those map calls on every render and to do them anywhere other than where you need them, since you only show using the result in a mount-only effect. Just do them there:
const { filteredResponsible } = useResponsible; // Is there really no `()` needed here?
useEffect(() => {
setValue("name", `${filteredResponsible.map(({name}) => name)}`);
setValue("email", `${filteredResponsible.map(({email}) => email)}`);
setValue("address", `${filteredResponsible.map(({address}) => address)}`);
// ...
}, []);
If you really need those distinct arrays on every render, unless you can change your data structures to be more amenable to your output I don't see you have a lot of options. You can at least avoid multiple loops through filteredResponsible:
const { filteredResponsible } = useResponsible; // ()?
const responsibleName = [];
const responsibleEmail = [];
const responsibleAddress = [];
for (const { name, email, address } of filteredResponsible) {
responsibleName.push(name);
responsibleEmail.push(email);
responsibleAddress.push(address);
}
And if that's really the case, you may want to avoid doing it on every render:
const { filteredResponsible } = useResponsible; // ()?
const { responsibleName, responsibleEmail, responsibleAddress } = useMemo(() => {
const responsibleName = [];
const responsibleEmail = [];
const responsibleAddress = [];
for (const { name, email, address } of filteredResponsible) {
responsibleName.push(name);
responsibleEmail.push(email);
responsibleAddress.push(address);
}
return { responsibleName, responsibleEmail, responsibleAddress };
}, [filteredResponsible]);
here i have created a function , it is framing a object by getting two values as arguments. It is working fine. But in the code wise is not simple. It is not readable. Here i am sharing the code of what i have done. Please suggest me where i have to modify the code. In order to make it more clear and readable.
import uuid from 'uuid/v4';
const questionsObject = {};
const finalDndDataFormat = {};
let value = [{"groups":[{"notes":"","questions":[{"name":"loanAmount"}]},{"questions":[{"name":"loanReason"}]},{"questions":[{"name":"email","type":"email"}]},{"questions":[{"name":"title","required":true}]},{"questions":[{"name":"firstName"},{"name":"lastName"}]}],"id":"my-needs"},{"groups":[{"questions":[{"name":"phone"},{"name":"isCellPhone"}]},{"questions":[{"name":"dateOfBirth","options":[]}]},{"questions":[{"name":"residenceStatus","options":[],"label":"q_ownRent_text","type":"radio","required":true}]},{"questions":[{"name":"addressLine1"},{"name":"addressLine2"},{"name":"city"},{"name":"province","options":[]},{"name":"postalCode"}],"title":"q2_supporting_2"},{"questions":[{"name":"employmentStatus","options":[]}]}],"id":"a-bit-about-me"},{"groups":[{"questions":[{"name":"residenceDuration","options":[]}]},{"questions":[{"name":"previousAddressLine1"},{"name":"previousAddressLine2"},{"name":"previousCity"},{"name":"previousProvince"},{"name":"previousPostalCode"}],"title":"q2_supporting_2"},{"questions":[{"name":"previousResidenceDuration"}]},{"questions":[{"name":"residenceStatus"}]},{"questions":[{"name":"principalResidence"}]},{"questions":[{"name":"residenceType"}]},{"questions":[{"name":"propertyValue"}]},{"questions":[{"name":"monthlyMortgagePayments"}]},{"questions":[{"name":"mortgageBalance"}]}],"id":"my-home"},{"groups":[{"questions":[{"name":"employmentDuration"}]},{"questions":[{"name":"previousEmploymentDuration"}]},{"questions":[{"name":"paycheckAmount"}]},{"questions":[{"name":"paycheckFrequency"}]},{"questions":[{"name":"hasAdditionalIncome"},{"name":"additionalIncomeSource"}]},{"questions":[{"name":"hasChildTaxCredit"},{"name":"childTaxCreditMonthlyAmount"},{"name":"numberOfDependentsChildTaxCredit"}]},{"questions":[{"name":"sinNumber"}]}],"id":"my-income"},{"groups":[{"questions":[{"name":"hasPaydayLoan","options":[]},{"name":"paydayLoanAmountOwing"}]},{"questions":[{"name":"hasOtherLiabilities","options":[]},{"name":"otherLiabilitySource","options":[]}]},{"questions":[{"name":"isEasyHomeCustomer","options":[]},{"name":"easyhomePaymentsLiabilityMonthlyAmount"}]}],"id":"liabilities"},{"groups":[{"notes":"","questions":[{"name":"agreeTermsConditionsAndPrivacyPolicy","options":[],"links":[]}]}],"id":"t-and-c"}]
let appPages = ["my-needs", "a-bit-about-me", "my-home", "my-income", "liabilities", "t-and-c"]
const setDndDataStructure = (value, appPages) => {
const dndFormat = value?.map((data) => {
appPages.forEach((pages) => {
switch (data?.id) {
case pages:
const questions = data?.groups || {};
questions.map((obj) => {
obj.id = uuid();
const uniqueQuestionName = obj?.questions?.map((questionsField) => questionsField?.name);
obj.unique = uniqueQuestionName[0];
});
const dndDataFormat = { [pages]: { name: pages, items: questions } };
Object.assign(questionsObject, dndDataFormat);
Object.assign(finalDndDataFormat, questionsObject);
break;
default:
break;
}
});
});
return finalDndDataFormat;
};
setDndDataStructure( value , appPages )
Result:
{"my-needs":{"name":"my-needs","items":[{"notes":"","questions":[{"name":"loanAmount"}],"id":"1b356c8f-0087-4929-8894-2a929aa25c6c","unique":"loanAmount"},{"questions":[{"name":"loanReason"}],"id":"59a57164-d945-4747-a429-763a38da61d5","unique":"loanReason"},{"questions":[{"name":"email","type":"email"}],"id":"d526fb2f-8612-4313-a4be-b1779817ccd2","unique":"email"},{"questions":[{"name":"title","required":true}],"id":"420028a5-a280-4fb4-96f6-a3fa464ad531","unique":"title"},{"questions":[{"name":"firstName"},{"name":"lastName"}],"id":"fe2f12e9-5c66-4b6b-ab82-ec7d3fe50226","unique":"firstName"}]},"a-bit-about-me":{"name":"a-bit-about-me","items":[{"questions":[{"name":"phone"},{"name":"isCellPhone"}],"id":"d74a5ba2-30a8-473b-8db9-bf82b3f2a6e9","unique":"phone"},{"questions":[{"name":"dateOfBirth","options":[]}],"id":"86ae162d-beb7-4f1a-a74b-c161515f3144","unique":"dateOfBirth"},{"questions":[{"name":"residenceStatus","options":[],"label":"q_ownRent_text","type":"radio","required":true}],"id":"0e5b151d-46f8-460d-862a-3e75aa6438df","unique":"residenceStatus"},{"questions":[{"name":"addressLine1"},{"name":"addressLine2"},{"name":"city"},{"name":"province","options":[]},{"name":"postalCode"}],"title":"q2_supporting_2","id":"31120f92-771d-4136-b465-ad7abf944718","unique":"addressLine1"},{"questions":[{"name":"employmentStatus","options":[]}],"id":"03796c7b-7ff9-4abb-9fe3-f01db45de4f3","unique":"employmentStatus"}]},"my-home":{"name":"my-home","items":[{"questions":[{"name":"residenceDuration","options":[]}],"id":"65ce7da7-005e-4f62-ba5a-0768afa01111","unique":"residenceDuration"},{"questions":[{"name":"previousAddressLine1"},{"name":"previousAddressLine2"},{"name":"previousCity"},{"name":"previousProvince"},{"name":"previousPostalCode"}],"title":"q2_supporting_2","id":"e9682e67-8e03-4ffd-b695-a8bc50dca98d","unique":"previousAddressLine1"},{"questions":[{"name":"previousResidenceDuration"}],"id":"f473e302-469b-4196-aa1a-00da1bdb4cf6","unique":"previousResidenceDuration"},{"questions":[{"name":"residenceStatus"}],"id":"c3f4f424-9619-458f-9e20-343247835ecd","unique":"residenceStatus"},{"questions":[{"name":"principalResidence"}],"id":"6c6d9aa8-e693-40f4-b805-b1333c6e97f1","unique":"principalResidence"},{"questions":[{"name":"residenceType"}],"id":"1946d5fd-86ae-4c18-aeb6-7f03d2cfc3e1","unique":"residenceType"},{"questions":[{"name":"propertyValue"}],"id":"27ed276a-e42c-4a9a-923e-fdf953ef4e56","unique":"propertyValue"},{"questions":[{"name":"monthlyMortgagePayments"}],"id":"38b306c2-4328-4c10-9281-e736348c4047","unique":"monthlyMortgagePayments"},{"questions":[{"name":"mortgageBalance"}],"id":"b67b7f19-caef-4404-9592-0c4293d92c4d","unique":"mortgageBalance"}]},"my-income":{"name":"my-income","items":[{"questions":[{"name":"employmentDuration"}],"id":"ad78527d-c5d9-42f3-879e-79c78c10c72d","unique":"employmentDuration"},{"questions":[{"name":"previousEmploymentDuration"}],"id":"596d9c81-5549-4e98-935d-faf65cd91b8d","unique":"previousEmploymentDuration"},{"questions":[{"name":"paycheckAmount"}],"id":"076827a0-1e77-4455-bfb5-d0d7c59fd34c","unique":"paycheckAmount"},{"questions":[{"name":"paycheckFrequency"}],"id":"709704d6-5451-4fcb-93d1-c11090d789b4","unique":"paycheckFrequency"},{"questions":[{"name":"hasAdditionalIncome"},{"name":"additionalIncomeSource"}],"id":"a17cde6c-6a3f-4792-bfc6-bafa2237cf1c","unique":"hasAdditionalIncome"},{"questions":[{"name":"hasChildTaxCredit"},{"name":"childTaxCreditMonthlyAmount"},{"name":"numberOfDependentsChildTaxCredit"}],"id":"a138e279-712b-4e0a-9adb-30c4b89f754f","unique":"hasChildTaxCredit"},{"questions":[{"name":"sinNumber"}],"id":"ab5e04ac-3cd7-431f-adbf-a63ecf410af0","unique":"sinNumber"}]},"liabilities":{"name":"liabilities","items":[{"questions":[{"name":"hasPaydayLoan","options":[]},{"name":"paydayLoanAmountOwing"}],"id":"6aedc455-1204-4dfb-8f76-97c4bdc0b66b","unique":"hasPaydayLoan"},{"questions":[{"name":"hasOtherLiabilities","options":[]},{"name":"otherLiabilitySource","options":[]}],"id":"fda26736-db4f-4a38-b122-dd6a898be9df","unique":"hasOtherLiabilities"},{"questions":[{"name":"isEasyHomeCustomer","options":[]},{"name":"easyhomePaymentsLiabilityMonthlyAmount"}],"id":"fda94364-9b90-4a93-80ac-698415a5207d","unique":"isEasyHomeCustomer"}]},"t-and-c":{"name":"t-and-c","items":[{"notes":"","questions":[{"name":"agreeTermsConditionsAndPrivacyPolicy","options":[],"links":[]}],"id":"b7c74dce-1251-4831-8066-65cb73a598c7","unique":"agreeTermsConditionsAndPrivacyPolicy"}]}}
Above code is the sample i have done.
Thanks in advance
Just add a few comments
//At the end, setDnDStructure will leave x structured as y
const setDndDataStructure = (value, appPages) => {
const dndFormat = value?.map((data) => { // mapping x to y so that z
appPages.forEach((pages) => {
switch (data?.id) {
case pages:
const questions = data?.groups || {};
questions.map((obj) => {
obj.id = uuid();
const uniqueQuestionName = obj?.questions?.map((questionsField) => questionsField?.name);
obj.unique = uniqueQuestionName[0];
});
const dndDataFormat = { [pages]: { name: pages, items: questions } };
Object.assign(questionsObject, dndDataFormat);
Object.assign(finalDndDataFormat, questionsObject);
break;
default:
break;
}
});
});
return finalDndDataFormat;
};
The comments I've added should give you an idea of how you can make this more readable. Outline what you are trying to achieve at the end, and use comments in your function to outline what each step is doing
I learn react and js myself. please explain why this situation occurs. PS: excuse me for the large text, I tried to explain the problem as clearly as possible. thanks. Essence of the matter: set the initial state through the hook:
const [pokemon, setPokemon] = useState({
img: "",
name: "",
types: [],
abilities: [],
moveList: [],
weight: "",
height: "",
description: "",
genus: "",
chanceToCatch: "",
evolutionURL: ""
});
further I make api requests to get information from inside useEffect:
useEffect(() => {
const fetchData = async () => {
await Axios({
method: "GET",
url: urlPokemonAPI
})
.then(result => {
const pokemonResponse = result.data;
/* Pokemon Information */
const img = pokemonResponse.sprites.front_default;
const name = pokemonResponse.name;
const weight = Math.round(pokemonResponse.weight / 10);
const height = pokemonResponse.height / 10;
const types = pokemonResponse.types.map(type => type.type.name);
const abilities = pokemonResponse.abilities.map(
ability => ability.ability.name
);
const moveList = pokemonResponse.moves.map(move => move.move.name);
setPokemon(() => {
return {
img: img,
name: name,
weight: weight,
types: types,
abilities: abilities,
moveList: moveList,
height: height
};
});
})
await Axios({
method: "GET",
url: urlPokemonSpecies
}).then(result => {
let description = "";
result.data.flavor_text_entries.forEach(flavor => {
if (flavor.language.name === "en") {
description = flavor.flavor_text;
}
});
let genus = "";
result.data.genera.forEach(genera => {
if (genera.language.name === "en") {
genus = genera.genus;
}
});
const evolutionURL = result.data.evolution_chain.url;
const eggGroups = result.data.egg_groups.map(
egg_group => egg_group.name
);
const chanceToCatch = Math.round(
(result.data.capture_rate * 100) / 255
);
setPokemon(pokemon => {
return {
...pokemon,
description: description,
genus: genus,
chanceToCatch: chanceToCatch,
evolutionURL: evolutionURL,
eggGroups: eggGroups
};
});
});
};
fetchData();
}, [urlPokemonAPI, urlPokemonSpecies]);
The problem arises specifically with eggGroups (with identical handling of abilities and types there is no such problem). And this is what happens when I want to output data to a page as <div> Egg Group: {pokemon.eggGroups} </div> the data is displayed normally, but as soon as I want to output eggGroups as well as abilities and types separated by commas (join ( ',')) - error: TypeError: pokemon.eggGroups is undefined. I decided to check this matter through the console and stuffed this eggGroups key into the timeout:
At some point, eggGroups becomes undefined ... why, I can’t understand. But if I set the state separately, like const [egg, setEgg] = useState ([]); setEgg (eggGroups); such a problem is not observed. why is this happening? everything was fine with types and abilities. Thank you in advance.
state updater from hooks doesn't merge the state values when updating state, instead it just replaces the old value with new one
Since you use state updater like
setPokemon(() => {
return {
img: img,
name: name,
weight: weight,
types: types,
abilities: abilities,
moveList: moveList,
height: height
};
});
eggGroups property is lost and hence it becomes undefined. You need to update it by spreading the previous state values obtained from callback
setPokemon((prev) => {
return {
...prev
img: img,
name: name,
weight: weight,
types: types,
abilities: abilities,
moveList: moveList,
height: height
};
});
Your code have a problem, this is the proper way to do await with axios,
you need to import axios like this
import axios from 'axios';
the await should be call with a promise, then it return the data from api like this:
const result = await axios.get(urlPokemonAPI);
This is the code snippet with the same logic to your code
useEffect(() => {
const fetchData = async () => {
// import axios from 'axios';
try {
const result = await axios.get(urlPokemonAPI);
const pokemon = result.data;
setPokemon({
img: pokemon.sprites.front_default,
name: pokemon.name,
weight: Math.round(pokemon.weight / 10),
types: pokemon.types.map(i => i.type.name),
abilities: pokemon.abilities.map(i => i.ability.name),
moveList: pokemon.moves.map(i => i.move.name),
height: pokemon.height / 10
});
const result2 = await axios.get(urlPokemonSpecies);
const data = result2.data;
let description = "";
data.flavor_text_entries.forEach(i => {
const lang = i.language.name
if (lang === "en") {
description = i.flavor_text;
}
});
let genus = "";
data.genera.forEach(i => {
const lang = i.language.name;
if (lang === "en") {
genus = i.genus;
}
});
setPokemon(pokemon => {
return {
...pokemon,
description,
genus,
chanceToCatch: Math.round((data.capture_rate * 100) / 255),
evolutionURL,
eggGroups: data.egg_groups.map(g => g.name)
};
});
} catch (e) {
console.log(e);
}
};
fetchData();
}, [urlPokemonAPI, urlPokemonSpecies]);
do you see another problem: you call setPokemon two times, let's rewrite it again:
useEffect(() => {
const fetchData = async () => {
// import axios from 'axios';
try {
const result = await axios.get(urlPokemonAPI);
const data1 = result.data;
const result2 = await axios.get(urlPokemonSpecies);
const data2 = result2.data;
function resolveDescription(data) {
let description = "";
data.flavor_text_entries.forEach(i => {
const lang = i.language.name
if (lang === "en") {
description = i.flavor_text;
}
});
return description;
}
function resolveGenus(data) {
let genus = "";
data.genera.forEach(i => {
const lang = i.language.name;
if (lang === "en") {
genus = i.genus;
}
});
return genus;
}
setPokemon({
img: data1.sprites.front_default,
name: data1.name,
weight: Math.round(data1.weight / 10),
types: data1.types.map(i => i.type.name),
abilities: data1.abilities.map(i => i.ability.name),
moveList: data1.moves.map(i => i.move.name),
height: data1.height / 10,
description: resolveDescription(data2),
genus: resolveGenus(data2),
chanceToCatch: Math.round((data2.capture_rate * 100) / 255),
evolutionURL: data2.evolution_chain.url,
eggGroups: data2.egg_groups.map(g => g.name)
});
} catch (e) {
console.log(e);
}
};
fetchData();
}, [urlPokemonAPI, urlPokemonSpecies]);
Can someone provide an example on how to use the beforeEach? http://www.node-tap.org/api/
Ideally, an example of the promise version, but a callback version example would also be nice.
Here is a test I created which works fine:
'use strict';
const t = require('tap');
const tp = require('tapromise');
const app = require('../../../server/server');
const Team = app.models.Team;
t.test('crupdate', t => {
t = tp(t);
const existingId = '123';
const existingData = {externalId: existingId, botId: 'b123'};
const existingTeam = Team.create(existingData);
return existingTeam.then(() => {
stubCreate();
const newId = 'not 123'
const newData = {externalId: newId, whatever: 'value'};
const newResult = Team.crupdate({externalId: newId}, newData);
const existingResult = Team.crupdate({externalId: existingId}, existingData);
return Promise.all([
t.equal(newResult, newData, 'Creates new Team when the external ID is different'),
t.match(existingResult, existingTeam, 'Finds existing Team when the external ID exists')
]);
});
})
.then(() => {
process.exit();
})
.catch(t.threw);
function stubCreate() {
Team.create = data => Promise.resolve(data);
}
Before I do anything, I want to persist existingTeam. After it's saved, I want to stub Team.create. After these two things, I want to start actually testing. I think it would be cleaner if instead of using a Promise.all or perhaps duplicating the test code, I could use beforeEach.
How would I convert this to use beforeEach? Or what is an example of its usage?
Simple, just return promise from callback function
const t = require('tap');
const tp = require('tapromise');
const app = require('../../../server/server');
const Team = app.models.Team;
const existingId = '123';
const existingData = {
externalId: existingId,
botId: 'b123'
};
t.beforeEach(() => {
return Team.create(existingData).then(() => stubCreate());
});
t.test('crupdate', t => {
t = tp(t);
const newId = 'not 123'
const newData = {
externalId: newId,
whatever: 'value'
};
const newResult = Team.crupdate({
externalId: newId
}, newData);
const existingResult = Team.crupdate({
externalId: existingId
}, existingData);
return Promise.all([
t.equal(newResult, newData, 'Creates new Team when the external ID is different'),
t.match(existingResult, existingTeam, 'Finds existing Team when the external ID exists')
]);
}).then(() => {
process.exit();
}).catch(t.threw);
function stubCreate() {
Team.create = data => Promise.resolve(data);
}