I'm searching for a shorter version to iterate through components from a JSON object
["Component1", "Component2", "Component3"]
The Index should be the step number and the component should be outputted dynamically. Right now I have a static way, which will become very uncomfortable with more elements:
<div">
{step === 1 && <Component1 />}
{step === 2 && <Component2 />}
{step === 3 && <Component3 />}
</div>
Does anyone knows a solution to this one?
Best regards!
You can use an array or an object to map key to its value (the indexes are keys here):
const components = [<Component1/>,<Component2/>,<Component3/>]
<div>{components[step]}</div>
The above components invoked in the array (meaning, although only a single component used, all elements called React.createElement) , to resemble the conditional rendering save a function component instead:
const functionComponents = [() => <Component1/>, () => <Component2/>,() => <Component3/>]
const Component = functionComponents[step];
<div><Component/></div>
Related
I have a function component that takes a variable amount of child (forwardRef) function components. What I would like to achieve is having a ref to each of the child components for animations when a child component is clicked. I have a semi-working solution by creating an array of refs and then cloning all the children and passing an indexed ref to each of them. The only issue is that all of the refs in the index point to the same (last) child.
Here are my components:
const Navbar = ({children}) => {
const [activeLink, setActiveLink] = useState(0);
const linkRefs = Array(children.length).fill(React.createRef(null));
const handleActiveLinkChange = (index) => {
setActiveLink(index);
console.log(linkRefs[index].current);
}
return (
<nav>
{React.Children.map(children, (child, index) => React.cloneElement(child, {index: index, active: index === activeLink, handleActiveLinkChange, key: "child" + index, ref: linkRefs[index]}))}
</nav>
)
}
const Link = React.forwardRef(({children, active, index, handleActiveLinkChange}, ref) => {
return (
<a href="#" style={linkStyle} onClick={() => handleActiveLinkChange(index)} ref={ref}>
{active ? <b>{children}</b> : children}
</a>
)
});
And assuming I use the components in the following way:
<Navbar>
<Link>One</Link>
<Link>Two</Link>
<Link>Three</Link>
<Link>Four</Link>
<Link>Five</Link>
</Navbar>
I expect the refs to be:
Ref array index
Ref current
0
One
1
Two
2
Three
3
Four
4
Five
But the refs I get are:
Ref array index
Ref current
0
Five
1
Five
2
Five
3
Five
4
Five
I'm assuming it's something to do with variable scope but I just can't figure out the cause of the issue. I've tried many variations of loops and functions but I'd rather understand the cause than blindly try to find a solution.
The issue is with the following line. It creates only one ref and all the array indices refer to that single ref.
const linkRefs = Array(children.length).fill(React.createRef(null));
Instead of the above use the following line which creates new refs for each child as you expect.
const linkRefs = Array.from({ length: children.length }, () =>
React.createRef(null)
);
EDIT: Obsolete, I made some mistake in another piece of code and the received data had the wrong data type.
I have a variable that stores the index of a selected item. I used a conditional expression based on that variable to add/remove a class name so said item is rendered in a distinguishing way. I boiled down the problem to this snippet:
function App() {
const [selectedItem, setSelectedItem] = setState(-1);
setSelectedItem(0);
console.log(selectedItem);
return (
<>
{selectedItem !== 0 && <p>no item selected</p>}
{selectedItem === 0 && <p>item {selectedItem} selected</p>}
</>
);
}
This snippet always displays that no item is selected.
The hook is called useState, not setState.
Calling setSelectedItem inside the render function will trigger an infinite loop. You need to move it to an effect hook so it only runs one.
export default function App() {
const [selectedItem, setSelectedItem] = useState(-1);
useEffect(() => {
setSelectedItem(0);
}, []);
console.log(selectedItem);
return (
<>
{selectedItem !== 0 && <p>no item selected</p>}
{selectedItem === 0 && <p>item {selectedItem} selected</p>}
</>
);
}
What is setState ? Do you mean useState ? And also, you shouldn't update the state like this, you should do it in a useEffect, and use an empty array as dependency, so it will run only once, when your component is being mounted:
useEffect(() => {
setSelectedItem(0);
},[])
Then it is working like a charm
Replace === and !== with == and !=, respectively, and voila, it works. Alas, a warning is reported that one shall use === instead.
selectedItem is an array, apparently primitives make bad references. Still, it is bizarre to some extent that inside <p> the variable is unboxed automatically, while for evaluating === it isn't and thus an array is compared to a primitive, which is not true, no matter the values. == compares every shit in JS that you feed it with, so here it works.
Hope, this saves somebody 2 hours of debugging.
If somebody has a correct workaround for this, please share below.
I need to replicate a component "n" times. To do that I used the lodash method times. The problem is that I need an index as a key for the components generated and It doesn't look like it has one.
I have the following code:
export const MyComponent: React.FC<{times: number}> = ({ times }) => {
return (
<>
{_.times(times, () => (
//I need a key={index} in this div
<div className="bg-white border-4 border-white md:rounded-md md:p-2 content-center my-4 shadow w-full">
</div>
))}
</>
);
};
This will return the component that is inside n times.
I tried to do a method that returns the component and set an index with useState, but it goes in an infinite loop. I thought to put a big random number as a key so it is extremely difficult to get the same, but I don't like that solution. I'd like to use this method because it is clean.
So what do you think I could do to give a to the component?
It's passed to you as a function parameter:
_.times(times, (index) => (blabla))
I am fetching data from an api and I need to render a component based on an if statement and I cant seem to figure it out. A customer has an array of roles. Customer.items is an array of customer objects. This is the if statement I am trying but doesnt work:
{customers?.items?.length > 1 && !roles.includes("Super") && (...component
Basically I need to check if roles array has "Super" and customers.items only has one element then dont render the component.
Also if roles is "Super" and customer.items.length > 1 then still render the component
customers.items: [{id: 2, name: "G"}, {id: 3, name: "H"}]
roles: ["Super", "Admin"]
This will render the component in all cases except when customers.items has only one element and if the roles include 'Super'.
const hasSingleCustomer = customers?.items?.length === 1
const hasSuperRole = roles.includes('Super'))
{!(hasSingleCustomer && hasSuperRole) && <Component />}
You can also write it as {(!hasSingleCustomer || !hasSuperRole) && <Component />} if you prefer.
You can try this approach
{(customers.items.length > 1 && roles.includes("Super")) ? <If Success Component/> : <Failure Component>}
I have written as per your request, as I am checking if the roles array has "Super" in it, You can still manipulate the operation inside the brackets(), and we have to use ? and : to make sure the conditions work,
Happy Coding :)
My suggestion is to split the equation/ conditions into smaller variables and then use them to create a validity condition. This way, your code is more readable and easier to maintain
const length = customers.items.length
const isSuperUser = roles.includes('Super')
const isAdminUser = roles.includes('Admin')
const isAllowedForSuper = isSuperUser && length === 1
const isAllowedForAdmin = isAdminUser && length === 0
if (isAllowedForSuper || isAllowedForAdmin) {
return <Component {...props} />
}
return null
I have the following layout.
<ParentComponent>
|ComponentA
|ComponentA
|ComponentA
Basically I call the same Component 3 times in the parent component because they are very similar but just have different values. I was told I need to put a button on the parent component to change the values of just one out of the 3 ComponentA. ComponentA is called 3 times to give me the same thing 3 times, if I make a button to set the state of one, it will go to all 3. How can I single out one component?
Thanks!
Make an array of props you want to send to your components, and then change the content of that array on some button/input events, as your application demands it.
For example:
const arrOfProps = [{someProp:'aaa'}, null, null]
const someFunc = (num)=>{
arrOfProps.forEach((x, index)=>{
if (index === num) {x = {someProp:'aaa'}}
else { x = null}
})
}
render(){
return(
<div>
<button onClick={()=>someFunc(0)}>One</button>
<button onClick={()=>someFunc(1)}>Two</button>
<button onClick={()=>someFunc(2)}>Three</button>
<ParentComponent>
<div>{arrOfComponents.map((comp, index)=>{
return <ComponentA key={index} prop={comp}/>
})}</div>
</ParentComponent>
</div>)
}