repeated data array .map () react - arrays

can someone tell me why my .map () array is returning the same data several times on thescreen? I would like to show it only once but I don't know why it is repeating,
Can someone help me?
React code below:
console.log('>>> [HistoricosAtendimento] props: ', props);
const viewData = props.historicos || [];
const convertToArray = Object.values(viewData);
return (
<>
<SC.Container>
<SC.Item>
{convertToArray.map((item) => {
console.log('convertToArray', convertToArray);
return (
<SC.Item key={item.protocolo}>
<SC.Description>{item.textNotas}</SC.Description>
</SC.Item>
);
})}
</SC.Item>

Just remove the outer wrapping component:
<SC.Container>
// <SC.Item> <= remove
{convertToArray.map((item) => {
// console.log('convertToArray', convertToArray); // should move out of JSX
return (
<SC.Item key={item.protocolo}>
<SC.Description>{item.textNotas}</SC.Description>
</SC.Item>
);
})}
// </SC.Item> <= remove

Related

Trouble converting a class to function in React Native

I'm trying to rewrite this code from class to a function. I've never used classes but I got this test code for a calendar app while learning react native but I seem to get stuck somewhere when I'm trying to replace componentDidUpdate to useEffect.
This is the old code:
export default class DaysInMonth extends React.PureComponent{
state = {
lastCalendarDayIndex: 0,
currentCalendarDayIndex: 0,
}
changeCurrentCalendarDayIndex = (index) => {
this.setState({
currentCalendarDayIndex: index
})
}
componentDidUpdate(prevProps, prevState){
if(this.state.currentCalendarDayIndex !== prevState.currentCalendarDayIndex){
this.setState({
lastCalendarDayIndex: prevState.currentCalendarDayIndex
})
}
}
render(){
return(
<>
{
this.props.row_days_array.map((rowData, index) => (
<CalendarRow
key = {'calendar row ' + index}
rowData = {rowData}
lastCalendarDayIndex = {this.state.lastCalendarDayIndex}
changeCurrentCalendarDayIndex = {this.changeCurrentCalendarDayIndex}
month_index = {this.props.month_index}
current_month_index = {this.props.current_month_index}
chooseDifferentMonth = {this.props.chooseDifferentMonth}
/>
))
}
</>
)
}
}
And this is the new code everything works except for some functions which has to do with the useEffect, I don't understand what properties I should add to get the same functionality as before. Thanks
export default function DaysInMonth({row_days_array, month_index, current_month_index, chooseDifferentMonth}) {
const [lastCalendarDayIndex, setLastCalendarDayIndex] = useState(0)
const [currentCalendarDayIndex, setCurrentCalendarDayIndex] = useState(0)
const changeCurrentCalendarDayIndex = (index) => {
setCurrentCalendarDayIndex(index)
}
useEffect(() => {
if(currentCalendarDayIndex !== currentCalendarDayIndex){
setLastCalendarDayIndex(currentCalendarDayIndex)
}
},[]);
return (
<>
{row_days_array.map((rowData, index) => (
<CalendarRow
key = {'calendar row ' + index}
rowData = {rowData}
lastCalendarDayIndex = {lastCalendarDayIndex}
changeCurrentCalendarDayIndex = {changeCurrentCalendarDayIndex}
month_index = {month_index}
current_month_index = {current_month_index}
chooseDifferentMonth = {chooseDifferentMonth}
/>
))}
</>
)
}
What this does:
useEffect(() => {
if(currentCalendarDayIndex !== currentCalendarDayIndex){
setLastCalendarDayIndex(currentCalendarDayIndex)
}
},[]);
It runs the code inside useEffect every time the array changes. Because it's an empty array it will just run once (once the component mounts, this is basically the old ComponentDidMount), to mimic the behaviour of ComponentDidUpdate you need to keep track of the props so it should be a matter of passing them into the array (so React can track when it changes):
useEffect(() => {
if(currentCalendarDayIndex !== currentCalendarDayIndex){
setLastCalendarDayIndex(currentCalendarDayIndex)
}
},[props]);
It's probably easier to change the destructuring you have in the component definition {row_days_array, month_index, current_month_index, chooseDifferentMonth} to props and then destructure a bit bellow, so you can use the whole object in you useEffect array
You can pass a callback in setState to access previous state, and pass currentCalendarDayIndex in useEffect dependency to update lastedCalendarState every currentCalendar changes. Hope this can help!
useEffect(() => {
setLastCalendarDayIndex((prevCalendarDayIndex) =>
prevCalendarDayIndex !== currentCalendarDayIndex
? currentCalendarDayIndex
: prevCalendarDayIndex)
}, [currentCalendarDayIndex])

How to Iterate and Get React State Object Values?

Here is my simple react component.
export default function App() {
const [stats, setStats] = React.useState(() => {
return {
name: "",
lvl: 1
}})
let displayStats;
for(let key in stats) {
displayStats += <p>{key}</p>
}
return (
<>
{displayStats}
</>
)
}
This displayStats variable returns 'undefined[object Object][object Object]' on the screen. Why? What am I doing wrong.
Change your code to this
return (
<>
{Object.keys(stats).map(key => <p>{key}</p>)}
</>
)
-> If you want to iterate through only values use Object.values(objName)
-> If you want to iterate through both values and keys use Object.entries(objName)
Try using the key on the stats object.
Change
displayStats += <p>{key}</p>
To
displayStats += <p>{stats[key]}</p>

how to render array in object in array? (react)

const checked = [{
food:['apple', 'banana']
drink:['wine', 'beer']
}];
render (
<>
{checked.map((value) => {
value.food.forEach((each) => (
<div>{each}</div>
)
)}
</>
)
I tried this way and noting is shown in browser..
what would be the best way to approach?
Need to Return Your data like below!!
import React from "react";
export default function App() {
let checked = [{
food:['apple', 'banana'],
drink:['wine', 'beer']
}];
return (
<div className="App">
{
checked.map((item) => {
return item.food.map((fruit)=>{
return <h1>{fruit}</h1>
})
})
}
</div>
);
}
Your code has multiple errors.
It should be render instead of rander
While defining object, multiple properties should be separated using a comma. So put comma after the food array.
forEach doesn't return any thing. It just iterates over an array. So, if you want to return something (in this case a div element), use map.
Also, you should use key for each div element otherwise react would give you a warning in the console. This is done so that while re-rendering, based on the keys, react would understand which component to re-render and which to skip. Otherwise all the div would be re-rendered which is a costly operation.
const checked = [
{
food: ["apple", "banana"],
drink: ["wine", "beer"]
}
]
return (
<>
{checked.map((value) => {
return value.food.map((each, index) => {
return <div key={index}>{each}</div>;
});
})}
</>
);
There is a couple of improvements that require to be implemented to make the list displayed.
First, the map method does not return anything.
Two solutions:
Remove the curly brackets checked.map((value) => value...
Add a return keyword: checked.map((value) => { return value...}
The other issue is that the second loop is iterated using the forEach method.
The difference between the two (forEach and map) from MDN:
The forEach() method executes a provided function once for each array
element.
MDN
The map() method creates a new array populated with the results of
calling a provided function on every element in the calling array.
MDN
Basically, it means that forEach does not return anything and that why you need to use map
checked.map((value) => {
return value.food.map((each) => (<div>{each}</div>))
})}
or
checked.map((value) =>
value.food.map((each) => (<div>{each}</div>))
)}
You are iterating over the checked array items using forEach which won't induce any results since the forEach method
executes a provided function once for each array element.
which won't result in a transformed array.
What you are looking for is the map method which
creates a new array populated with the results of calling a provided function on every element in the calling array.
hence returning your transformed items so that they can be rendered (transformed at compilation time to ReactElement using the JSX syntax).
Note that you need to use an HTML tag instead of a React.Fragment the empty tag <> syntax:
const checked = [{
food:['apple', 'banana'], // there is a missing comma here
drink:['wine', 'beer']
}];
render ( // render and not rander
<div> // div instead of empty tag
{checked.map((item) => item.food.map((each) => <div>{each}</div>))}
</div>
)
Can check this approach. if you want to print just food values, below code should work. If you want to print all the values (both food and drink), then uncomment the commented code below.
export default function App() {
const checked = [
{
food: ["apple", "banana"],
drink: ["wine", "beer"]
},
{
food: ["grapes", "oranges"],
drink: ["coke", "sprite"]
}
];
// to display values of all arrays.
// return (
// <>
// {checked.map((value) => {
// const keys = Object.keys(value);
// return keys.map((eachKey) => {
// return value[eachKey].map((individualValue) =>
// (
// <div>{individualValue}</div>
// )
// )
// });
// })
// }
// </>
// );
// To display just food values.
return (
<>
{checked.map((value) => {
return value.food.map((each) => <div>{each}</div>);
})}
</>
);
}

nesting components with createElement in React yields error

I'm trying to create a helper function that nest a passed element/component as a children of itself of several depths.
So something like this
const Chain = nested(4, () => <div />)
return (
<Chain />
)
Should render divs nested 4 levels
<div>
<div>
<div>
<div />
</div>
</div>
</div>
I implemented the function in React like this.
/** nest the given element in itself of specified depth */
export const nested = (
depth: number,
element: () => JSX.Element
): FC<any> => () => {
let chain = element();
for (let i = 0; i < depth; i++) {
chain = React.createElement(
element().type,
element().props,
chain
);
}
return chain;
};
It renders and displays the nested Divs correctly, but gives back an error message i don't understand how to correct.
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
Try going with a recursive solution.
const createInnerElements = (count, childElement) => {
if (count == 1) {
return <div>{childElement}</div>
}
return createInnerElements(count - 1, <div>{childElement}</div>);
}
// in your parent component,
return <>{createInnerElements(4, <></>)}</>
Here's the solution I settled with. Credit to #senthil balaji's answer.
export const NestedTags = (depth: number, childElement: JSX.Element): JSX.Element => {
if (depth === 1) {
return (
<childElement.type {...childElement.props}>
{childElement}
</childElement.type>
);
}
return NestedTags(
depth - 1,
<childElement.type {...childElement.props}>
{childElement}
</childElement.type>
);
};
and should be wrapped in a React Fragment using used in parent.
<>
{NestedTags(5, <div className='segment'><div>)}
</>
The caveat for this is that it can't nest when passed a React Component Type like so:
const Test = ({className}: {className: string}) => (
<a className={className}>hello</a>;
)
//...
return (
<>
{NestedTags(5, <Test className='test'/>}
</>
)};

How to conditionally add a child component to a parent component in React?

Consider an array like this:
const myArray = [1,2,3]
Depending on the length of this array (which keeps changing only between 2 and 3), I want to add or remove a react-native-svg component which display this value.
This is how I have approached it so far (I know this code will not work):
const myComponent = (props) => {
const [myArray, setMyArray] = useState([1,2,3]);
const mySVGComponents = useEffect(() => {
//It is ensured that the array has at least two values
let GroupWrapper = (() => {
return (
<G>
<SVGText>{myArray[0]}</SVGText>
<SVGText>{myArray[1]}</SVGText>
</G>
)
})
if (myArray.length === 3) {
GroupWrapper = (() => {
return (
<GroupWrapper>
<SVGText>{myArray[2]}</SVGText>
</GroupWrapper>
)
})
}
return GroupWrapper;
}, [myArray]);
return (
<G>
{mySVGComponents}
</G>
)
}
How can I inject SVGText components into the <G> component conditionally based on the length of myArray?
Rendering an array of items in React is not as complicated as you are trying to do it here. You can read mode about that here: https://reactjs.org/docs/lists-and-keys.html
Specifically for your case, you first have to remove the useEffect, you are not supposed to use it like that (https://reactjs.org/docs/hooks-effect.html). All you have to do is just map the array in the return statement, take a look at this:
export const MyComponent = (props) => {
const [myArray, setMyArray] = useState([1,2,3]);
return <G>{myArray.map(item => <SVGText key={item}>{item}</SVGText>)}</G>;
}
This way you are rendering each item in the array, and it will be automatically rerendered when you update you array using setMyArray.
Only change your "mySVGComponents" to below given code.
const mySVGComponents = useEffect(() => {
//It is ensured that the array has at least two values
let GroupWrapper = (() => {
return (
<G>
{
myArray.map(item => <SVGText>{item}</SVGText>)
}
</G>
)
})
return GroupWrapper;
}, [myArray]);

Resources