Click events are not available on the map of the react - javascript

{userList.map(v => (
<Card
key={v.user}
onClick={() =>
console.log("object")
}
>
<Card.Header
title={v.user}
thumb={require(`../img/${v[tableName].avatar}.png`)}
extra={<span>{v[tableName][extra]}</span>}
/>
<Card.Body>
{v[tableName].desc.split(/\n/g).map((v, k) => (
<li key={k}>{v}</li>
))}
</Card.Body>
</Card>
))}
Click events are not available on the .map() function in React

Card Component of React material UI does not support onClick props so you should not use it.
Quick Fix would be to enclose it inside a div and add onClick on the div as shown below:
<div onClick=> {() =>
console.log("object")
}>
<Card>
.....
</Card>
</div>
Or You can use some different library from :
https://material-ui.com/api/card-action-area/

Related

how to have multiple cards in each Carousel slide react-bootstrap

I'm working with react-bootstrap and I have my data coming from an API and saved in recipes as an array.
I'm trying to have Carousel with react-bootstrap but right now it shows each item on a separate slide, how can I show 3 items on each slide?
this is my code
<div>
<Wrapper>
<h3>Breakfast Recipes</h3>
<Carousel>
{recipes.map((item,index) => (
<Carousel.Item key={index}>
<Card style={{ width: '18rem' }}>
<Card.Img variant="top" src={item.photo_location} />
<Card.Body>
<Card.Title>{item.name}</Card.Title>
<Button variant="primary">Go somewhere</Button>
</Card.Body>
</Card>
</Carousel.Item>
))}
</Carousel>
</Wrapper>
</div>
I know that to do so I should have 3 <Carousel.Item> tags but I can't figure out how to do it with map
Assuming that the goal is to have 3 cards on each slide, it seems that instead of having 3 <Carousel.Item>, the desired output should probably be to have each <Carousel.Item> containing 3 <Card>.
Perhaps try use reduce on recipes to make an array that groups every 3 recipes to map a <Carousel.Item> for each group, and then nest another map inside to output 3 <Card>.
The below example also added a <div> to wrap the <Card> and place them horizontally. It used bootstrap syntax with assumption that the project also uses bootstrap classes.
But if inline styling is preferred, perhaps change <div className="d-flex justify-content-center"> to <div style={{display:"flex", justifyContent: "center"}}>.
Live demo of the example: stackblitz
A very basic example could be:
// Helper function to group every 3 recipe to a new array
const reduceRecipes = (acc, cur, index) => {
const groupIndex = Math.floor(index / 3);
if (!acc[groupIndex]) acc[groupIndex] = [];
acc[groupIndex].push(cur);
console.log(acc);
return acc;
};
return (
<div>
<Wrapper>
<h3>Breakfast Recipes</h3>
<Carousel>
{recipes.reduce(reduceRecipes, []).map((item, index) => (
<Carousel.Item key={index}>
<div className="d-flex justify-content-center">
{item.map((item, index) => {
return (
<Card key={index} style={{ width: "18rem" }}>
<Card.Img variant="top" src={item.photo_location} />
<Card.Body>
<Card.Title>{item.name}</Card.Title>
<Button variant="primary">Go somewhere</Button>
</Card.Body>
</Card>
);
})}
</div>
</Carousel.Item>
))}
</Carousel>
</Wrapper>
</div>
);
Some further custom styles should probably be added to make this work better, but hope that this still helps.

Re-ordering JSX including React's Context Provider

I'm wondering if there is a way of re-factoring this code so that RepeatableAttributeSetContextProvider does not wrap all of the JSX elements but sits above them (so opens and closes before StyledHorizontalAttributesTable). When I do that at present, then values like enteredObject: "cannot be defined".
I want to try this so that I can decouple of some of the JSX that comes along with RepeatableAttributeSetContextProvider (such as labels) away from the rest of my JSX (to sit on top instead of wrap) Any ideas would be great!
return (
<div className={className}>
{objects.map((enteredObject, index) => (
<RepeatableAttributeSetContextProvider
form={form}
object={enteredObject}
key={`${enteredObject.key}-${enteredObject.repeatIndex}`}
>
<StyledHorizontalAttributesTable className="attributeset-row">
{enteredObject.attributeCollection.questions
.filter(filterRepeatAttributes)
.map((attribute) => (
<Fragment key={attribute.key}>
{renderAttribute(enteredObject, attribute, formLayout)}
</Fragment>
))}
<StyledButton
className="remove-btn"
type="link"
buttonStyle="LINK"
name="delete"
dataId={`delete-${enteredObject.key}-${index}`}
icon="bin"
onClick={() => onRemove(enteredObject)}
>
<Message id="Form.Button.Remove" defaultMessage="Remove" />
</StyledButton>
</StyledHorizontalAttributesTable>
</RepeatableAttributeSetContextProvider>
))}
</div>
);
Do you mean something like
return (
<div className={className}>
{objects.map((enteredObject, index) => (
<>
<RepeatableAttributeSetContextProvider
form={form}
object={enteredObject}
key={`${enteredObject.key}-${enteredObject.repeatIndex}`}
/>
<StyledHorizontalAttributesTable className="attributeset-row">
{enteredObject.attributeCollection.questions
.filter(filterRepeatAttributes)
.map((attribute) => (
<Fragment key={attribute.key}>
{renderAttribute(enteredObject, attribute, formLayout)}
</Fragment>
))}
<StyledButton
className="remove-btn"
type="link"
buttonStyle="LINK"
name="delete"
dataId={`delete-${enteredObject.key}-${index}`}
icon="bin"
onClick={() => onRemove(enteredObject)}
>
<Message id="Form.Button.Remove" defaultMessage="Remove" />
</StyledButton>
</StyledHorizontalAttributesTable>
</>
))}
</div>
);
But then you obviously won't have any access to the (assumed) context provided by RepeatableAttributeSetContextProvider. If you do need it, then you do need to wrap your JSX within it. If you don't need it, then why have the component at all?

React beautiful dnd first column of two not working properly - unable to find draggable with id

I've implemented react beautiful dnd into my app, however I've found that dragging items that are initialised in the first column mostly doesn't work (it sometimes works if you keep refreshing). I tried swapping my columns around and it appears the first one rendered has the problem rather than one column having an issue.
I'm not sure what is causing it, the error I receive in the console from the library is:
Unable to find draggable with id: 6112804b8127dd10b00138d8.
The id however matches the ids in my lists and the HTML viewed in inspect element.
Here's the minimum code, I've not included the functions for filtering search, reordering, move and onDragEnd (isn't being fired as it doesn't find the draggable)
const id2List = {
'available': availableSections,
'selected': selectedSections
};
const getList = id => id2List[id];
return (
<Container>
<Widget title={template.name} paddingStyle="10px" buttonRight={<SaveButton handleSubmit={handleSubmit} loading={loading}/>}>
<DragDropContext onDragEnd={onDragEnd}>
<Row>
<Col >
Available Sections
<Form.Control onChange={(e) => setSearchTextAvailable(e.target.value)} className="mb-3" type="text" placeholder="Search..." />
<Droppable key="available" droppableId="available">
{provided => (
<div ref={provided.innerRef} {...provided.droppableProps}>
{Object.keys(visibleAvailableSections).map((section) => {return <SectionCard key ={visibleAvailableSections[section]._id} section={ visibleAvailableSections[section] } index={parseInt(availableSections.indexOf(visibleAvailableSections[section])) } /> })}
{provided.placeholder}
</div>
)}
</Droppable>
</Col>
<Col>
Selected Sections
<Form.Control onChange={(e) => setSearchTextSelected(e.target.value)} className="mb-3" type="text" placeholder="Search..." />
<Droppable key="selected" droppableId="selected">
{provided => (
<div ref={provided.innerRef} {...provided.droppableProps}>
{Object.keys(visibleSelectedSections).map((section) => {return <SectionCard key ={visibleSelectedSections[section]._id.toString()} section={ visibleSelectedSections[section] } index={parseInt(selectedSections.indexOf(visibleSelectedSections[section])) } /> })}
{provided.placeholder}
</div>
)}
</Droppable>
</Col>
</Row>
</DragDropContext>
</Widget>
</Container>
)
const SectionCard = ({section, index}) => {
return (
<Draggable draggableId={section._id.toString()} key={section._id} index={index}>
{provided => (
<Card key={"card-" + section._id.toString()} style={{ width: '18rem' }}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<Card.Body>
<Card.Title>{ section.name }</Card.Title>
<Card.Subtitle className="text-muted">{ section.description }</Card.Subtitle>
{ section.tags.map((tag) => {
return <Badge pill bg={"secondary"} key={section._id + tag} >{ tag }</Badge>
})}
</Card.Body>
</Card>
)}
</Draggable>
);
}

Why doesn't Next.js Json.map work properly?

I want to build a simple component where the data comes from a JSON list. On the website I can see the console.log(dat.CardId) so it works for just console.log(). I can't see the cards from the .map function. The Style is bootstrap. I really don't think that something is wrong at the code.
import { Card,Button,Container,Row,Col } from 'react-bootstrap'
const data = [{'CardTitle':'Simple Website','CardText':"Some quick example text to build on the card title and make up the bulk of the card's content.", "CardId":"Simple-Website"},{'CardTitle':'Simple Website','CardText':"Some quick example text to build on the card title and make up the bulk of the card's content.", "CardId":"Simple-Website"} ]
export default function Cards() {
return (
<>
<div>
<Container fluid>
Cards
{data.map(dat => {
{console.log(dat.CardId)}
<Row>
<Col>
<Card style={{ width: '18rem' }}>
<Card.Img variant="top" src="pexels-tima-miroshnichenko-6612358 (2).jpg/100px180" />
<Card.Body>
<Card.Title>{dat.CardTitle}</Card.Title>
<Card.Text>
{dat.CardText}
</Card.Text>
<Button variant="primary">Go somewhere</Button>
</Card.Body>
</Card>
</Col>
</Row>
})}
</Container>
</div>
</>
)
}
Use return ( ... ) in a .map to render an element. https://stackoverflow.com/a/39999745/15257712
Example:
array.map((entry, index) => {
return (
<div key={index}>
...
</div>
)
})

Rendering each value from a firebase array into <li> with ReactJS

I'm working on a recipe app and I'm having some issues rendering my ingredient list from a Firebase array. I'm new to JS and React, so i apologize if this is something simple. I really appreciate any help that's provided! I've attached my code and an example of how the array is currently being rendered. I've also included a screenshot of the Firebase data structure. [firebaseStructure][1]
useEffect(() =>{
database.collection('recipes').onSnapshot(snapshot => {
setRecipeCard(snapshot.docs.map(doc => ({id:doc.id, recipes:doc.data()})));
console.log(snapshot.docs.map(doc => ({id:doc.id, recipes:doc.data()})))
})
}, []);
useEffect(() =>{
database.collection('recipes').onSnapshot(snapshot =>{
setIngredientsList(snapshot.docs.map(doc =>({id:doc.id, ingredient: doc.data().ingredients1})));
console.log(snapshot.docs.map(doc =>({id:doc.id, ingredient: doc.data().ingredients1})))
})
}, []);
return (
<div>
<Container>
<Row>
{recipeCard.map(recipes =>(
<Col md={4} key={recipes.id}>
<Card style={{ width: '100%' }}>
<Card.Img variant="top" src={recipes.recipes.url} style={{height:'300px',width:'100%'}} />
<Card.Body>
<Card.Title>{recipes.recipes.title}</Card.Title>
{/**<Card.Text> **/}
<ul key={recipes.id}>
{ingredientsList.map(recipes =>(
<li key={recipes.id}>
{recipes.ingredient}
</li>
))}
</ul>
{/** </Card.Text> **/}
<Button variant="secondary">Head to Recipe</Button>
</Card.Body>
</Card>
</Col>
))}
</Row>
</Container>
</div>
)
}
[current rendered output][2]
[1]: https://i.stack.imgur.com/bFBdr.png
[2]: https://i.stack.imgur.com/xU1fC.png
I think a lot of the confusion here has come from the naming convention, and the attempt to use separate state for the recipieCard and ingredientsList. You've ended up rendering all the ingredients for all recipies instead of just the one's relevant to that recipie. If you ever find yourself accessing properties like recipies.recipies, you know you need to do some refactoring (if you have complete control of the project).
First things first, you only need one useEffect to access your database and process the data. It all belongs together, so there's no need to separate it into separate states. Notice how I've renamed the state as recipieCards plural. This is an array or list of data objects, so it should have a pluralised name. Each data object now has the property data instead of recipies, which more accurately describes what it contains (i.e. the data for ONE recipie).
useEffect(() =>{
database.collection('recipes').onSnapshot(snapshot => {
setRecipeCards(snapshot.docs.map(doc => ({id:doc.id, data:doc.data()})));
console.log(snapshot.docs.map(doc => ({id:doc.id, data:doc.data()})))
})
}, []);
Now in the return fuction it becomes much more clear what's going on. We take the array of recipieCards and map over it to access each recipie data object one at a time. For each data object use the id as its key, and take the url and title from data. Now you can access that recipie's ingredients directly from the same data object, under the property data.ingredients1.
return (
<div>
<Container>
<Row>
{recipeCards.map(recipe =>(
<Col md={4} key={recipe.id}>
<Card style={{ width: '100%' }}>
<Card.Img variant="top" src={recipe.data.url} style={{height:'300px',width:'100%'}} />
<Card.Body>
<Card.Title>{recipe.data.title}</Card.Title>
{/**<Card.Text> **/}
<ul>
{recipie.data.ingredients1.map(ingredient =>(
<li key={ingredient}>
{ingredient}
</li>
))}
</ul>
{/** </Card.Text> **/}
<Button variant="secondary">Head to Recipe</Button>
</Card.Body>
</Card>
</Col>
))}
</Row>
</Container>
</div>
);
You don't need to call the DB twice in two separate useEffect to do what you want. You should only call the DB once for efficiency in this case.
Also, you're re declaring recipes within the second map, this could cause errors, you should avoid using the same variable name for different variables. Your problem may be related to this. I edited the following code assuming you only use the first useEffect with setRecipeCard.
{recipeCard.map(recipe =>(
<Col md={4} key={recipe.id}>
<Card style={{ width: '100%' }}>
<Card.Img variant="top" src={recipe.recipes.url} style={{height:'300px',width:'100%'}} />
<Card.Body>
<Card.Title>{recipe.recipes.title}</Card.Title>
{/**<Card.Text> **/}
<ul key={recipe.id}>
{recipe.ingredients1.map(ingredient =>(
<li key={recipes.id}>
{ingredient}
</li>
))}
</ul>
{/** </Card.Text> **/}
<Button variant="secondary">Head to Recipe</Button>
</Card.Body>
</Card>
</Col>
))}
Can you try out this code? Please let us know specifically if you are getting any errors :)

Categories