I need to render one jsx component for each item in an array.
import { ListView } from './components'; // Custom component
const array = ["item1","item2","item3"];
export default function App() {
return(
<div>
{/* Here i want to render a <ListView /> component for each of the items in the array. */}
</div>
);
}
Here there are 3 items in the array so i want to render 3 different components.
Any help will be appreciated.
Try to use map method:
return (
<div>{ array.map(item => <ListView key={item} item={item} /> }</div>
)
Related
I'm trying to build a React select component that can support multiple different child component types.
I'd like to do something like this:
export const GenericSelect = (props) => {
const { component, items } = props;
return <>{items && items.map((item, index) => <component id={items.id} name={item.name} />)}</>;
};
And then be able to use it like:
<GenericSelect component={NonGenericCard} items={items} />
Where NonGenericCard supports a fixed set of properties (e.g., id, name), which will be populated from values in the items object.
I tried this, but it doesn't seem like it can create the <component/> at run-time.
Is this possible in Javascript? If so, how can it be accomplished?
In JSX, lower-case tag names are considered to be HTML tags. So you should use Component instead of component.
Also id should be item.id instead of items.id and you should give each element a key.
export const GenericSelect = (props) => {
const { Component, items } = props;
return (
<>
{items &&
items.map((item, index) => (
<Component key={item.id} id={item.id} name={item.name} />
))}
</>
);
};
<GenericSelect Component={NonGenericCard} items={items} />
https://reactjs.org/docs/jsx-in-depth.html#user-defined-components-must-be-capitalized
after getting all mixed up with state, i am now trying to restructure my app in a way that might be more reflective of best practices (not sure if this is the way, advice is welcome.)
so, i have my main page, which holds 3 states: viewer,buyside,sellside
there are also three different components, one for each of those states.
i want to be able to pass the props down from the main page, through those components, to their children (i've read this is the best approach??)
main page:
//we have 3 states for the website: viewer,buyside customer, sellside customer
const [visitorType, setVisitorType] = useState('viewer');
if (visitorType == 'viewer') {
return(
<div>
<Viewer visitortype='viewer' setvisitor={()=>setVisitorType()}/>
</div>
)}
else if (visitorType =='buyside') {
return(
<div>
<Buyside visitortype='buyside' setvisitor={()=>setVisitorType()}/>
</div>
)}
else if (visitorType =='sellside') {
return(
<div>
<Sellside visitortype='sellside' setvisitor={()=>setVisitorType()}/>
</div>
)}
};
what is the best way to pass down the main page props, so that i can bring them down to any grandchildren, along with the child props?
the viewer component -UPDATED-:
const MainView = (props) => {
return(
<>
<Navbar mainprops={{props}}/>
</>
)
};
export default MainView
i was previously just passing them individually, but realized it might be better to do so as one object...
UPDATE: point taken on the syntax, but i'm wondering how i can best pass the objects
nav component (grandchild)
const Navbar = (props) => {
const {mainprops} = props.mainprops;
if (mainprops.visitortype == 'viewer') {
return(
<>
<h1>viewer navbar</h1>
</>
)}
else if (mainprops.visitortype =='buyside') {
return(
<>
<h1>buyside navbar</h1>
</>
)}
else if (mainprops.visitortype =='sellside') {
return(
<>
<h1>sellside navbar</h1>
</>
)}
};
export default Navbar;
UPDATE 2 - this works, but not sure if it is the correct way, are these still considered object literals??
viewer component:
const MainView = (props) => {
const mainprops = {...props}
return(
<>
<Navbar mainprops={mainprops}/>
</>
)
};
export default MainView
navbar component
const Navbar = (props) => {
const mainprops = {...props.mainprops};
if (mainprops.visitortype == 'viewer') {
return(
<>
<h1>viewer navbar</h1>
</>
)}
else if (mainprops.visitortype =='buyside') {
return(
<>
<h1>buyside navbar</h1>
</>
)}
else if (mainprops.visitortype =='sellside') {
return(
<>
<h1>sellside navbar</h1>
</>
)}
};
export default Navbar;
if this is correct, then is this what #amir meant?
First there are certain rules for passing props:
You never ever pass literal object as props since it will not be the same every re-render and will cause the child component to re-render too (without any new info)
You don't need to do that
<Viewer visitortype='viewer' setvisitor={()=>setVisitorType()}/>
You can:
<Viewer visitortype='viewer' setvisitor={setVisitorType}/>
since it comes from useState react make sure the setVisitorType keeps the same reference
And now for you error, you almost correct you just did a js syntax error
you should write it like this:
const MainView = (props) => {
return(
<>
<Navbar mainobj={{
visitortype:props.visitortype,
setvisitor:props.setvisitor
}}
/>
</>
)
};
export default MainView
But again you never send literal object as props
I would keep it inside a ref or state (depend if the visitor state will be change)
I am trying to build a reusable accordion, i was able to create an accordion with one level, but here i am stuck to have the nested accordion.
What i have tried so far
App.js
import "./styles.css";
import Accordion from "./Accordion";
import LIST from './Constants';
const listMaker = (item) => {
let faqItem;
if (item.children.length === 0) {
faqItem = (
<>
<Accordion title={item.name}></Accordion> <hr />
</>
);
} else {
let faqItemChildren = item.children.map((item) => {
let faqItem = listMaker(item);
return (
<>
${faqItem}
<hr />
</>
);
});
faqItem = <Accordion title={item.name}>{faqItemChildren}</Accordion>;
}
return faqItem;
};
let listItems = LIST.map((item) => {
let menuItem = listMaker(item);
return menuItem;
});
export default function App() {
return listItems;
}
have added codesandbox
I am new tor react, Any help is appreciated
Instead of using dangerouslySetInnerHTML you can use the children, as you need is a spread of React.ReactChildren. That would be just calling the {children} from props instead of the dangerouslySetInnerHTML
<div className="accordion__section">
<button className={`accordion ${setActive}`} onClick={toggleAccordion}>
<p className="accordion__title">{title}</p>
<Chevron className={`${setRotate}`} width={10} fill={"#777"} />
</button>
<div
ref={content}
style={{ maxHeight: `${setHeight}` }}
className="accordion__content"
>
{children}
</div>
</div>
Here is a forked solution of your codesandbox.
Also, Instead of setting the DOM to a variable, as its a conditional scenario, you can use the ternary operator, which helps in better readability.
const listMaker = (item) => {
return (
<>
{item.children.length === 0 ? (
<Accordion title={item.name} />
) : (
<Accordion title={item.name}>
{item.children.map((childItem) => {
return listMaker(childItem);
})}
</Accordion>
)}
</>
);
};
dangerouslySetInnerHTML is to use with strings. You shouldn't give an array of components to it. Yet you don't send any prop called content anyway. I think you meant children prop there. Just render children instead of using dangerouslySetInnerHTML
In your Accordion component replace this:
<div
className="accordion__text"
dangerouslySetInnerHTML={{ __html: props.content }}
/>
With this:
<div className="accordion__text">
{ props.children }
</div>
So this is my code :
import React from "react";
import Navigation from './Navigation';
import Foot from './Foot';
import MovieCard from './MovieCard';
class Favorites extends React.Component {
render() {
const { onSearch, favorites, favoriteCallback, totalFavorites, searchKeyUpdate } = this.props;
return (
<div>
<Navigation
onSearch={onSearch}
totalFavorites={totalFavorites}
searchKeyUpdate={searchKeyUpdate} />
<div className="container">
<button onClick={()=> this.clearFavorites(favorites)}> Clear all movies </button>
{(favorites.length < 1) ?
<h1 style={{ fontSize: '13px', textAlign: 'center' }}>Please mark some of the movies as favorites!</h1>
:
<ul
className="movies">
{favorites
.map(movie => (
<MovieCard
movie={movie}
key={movie.imdbID}
toggleFavorite={favoriteCallback}
favorites={favorites}
/>
))}
</ul>
}
<Foot />
</div>
</div>
);
}
}
const clearFavorites = (favorites) => {
this.setState({ favorites: [] });
}
The thing I need for the button to do is that when i click it that it clears the whole state of favorites. The clearFavorites function is used to clear everything but when I try this I get an error:
Why doesn't this clear the state of favorites?
You have two problems:
clearFavorites function is not in your class. So you should put it inside.
You are trying to clear the data inside the favorites array, which is not part of your state, using the function clearFavorites. So, first of all, you should add favorites array to your state and then you can manipulate the information. I suggest you to use the function getDerivedStateFromProps.
As others mentioned, first moving clearFavorites function into Favorites class.
Second, your favorites list is not part of state object, but instead you pull it out from this.props.favorites, so instead of using this.setState, we should just change the props value.
Third, since you're emptying the array, the parameter in your clearFavorites probably not needed? Please refer to below:
First we define a constructor to get the value from props and pass it to state in the constructor as below:
constructor(props) {
super(props);
this.state = {favorites: this.props.favorites}
}
clearFavorites = () => {
this.setState({favorites: []});
};
Then at last in your render method change to following:
const { onSearch, favoriteCallback, totalFavorites, searchKeyUpdate } = this.props;
const favorites = this.state.favorites;// Or in your ul tag, instead of using favorites, change it to this.state.favorites
You can try to move the clearFavorites into your component
import React from "react";
import Navigation from "./Navigation";
import Foot from "./Foot";
import MovieCard from "./MovieCard";
class Favorites extends React.Component {
render() {
const {
onSearch,
favorites,
favoriteCallback,
totalFavorites,
searchKeyUpdate
} = this.props;
return (
<div>
<Navigation
onSearch={onSearch}
totalFavorites={totalFavorites}
searchKeyUpdate={searchKeyUpdate}
/>
<div className="container">
<button onClick={() => this.clearFavorites(favorites)}>
{" "}
Clear all movies{" "}
</button>
{favorites.length < 1 ? (
<h1 style={{ fontSize: "13px", textAlign: "center" }}>
Please mark some of the movies as favorites!
</h1>
) : (
<ul className="movies">
{favorites.map(movie => (
<MovieCard
movie={movie}
key={movie.imdbID}
toggleFavorite={favoriteCallback}
favorites={favorites}
/>
))}
</ul>
)}
<Foot />
</div>
</div>
);
}
clearFavorites = favorites => {
this.setState({ favorites: [] });
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
I have an array of objects. And I can't figure out how do I create multiple card decks with 3 cards in each one, as on the image:
An example is below:
import { Card, Button, ... CardBlock } from 'reactstrap';
export default const Example = (props) => {
return (<div>
<CardDeck>
<Card>
<CardImg top width="100%" src="https://..." />
<CardBlock>
<CardTitle>{data.title}</CardTitle>
<CardSubtitle>{data.subtitle}/CardSubtitle>
<CardText>{data.text}</CardText>
<Button>Button</Button>
</CardBlock>
</Card>
<Card>
...
</Card>
<Card>
...
</Card>
</CardDeck>
<CardDeck>
...
</CardDeck>
...
</div>
);
};
If your question is about how to iterate over your array then you can use Array.prototype.map() for it.
An example from the react docs with map() call using an arrow function:
By saving the components in a temporary variable:
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
By using map() inline:
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
EDIT:
Do not render multiple <CardDeck> components. Instead adapt your css so that a <Card> will wrap into the next row when there are more that three. Then you only have to map the data inside your array once and not for every <CardDeck> again.