This is my second day first day learning react and I came across problem that I could solve in a better way but I don't know how it is done in React.
import ExpenseItem from './ExpenseItem';
import './Expenses.css';
function Expenses(props) {
const { items } = props;
// const html = items
// .map(el => {
// return `
// <ExpenseItem
// title={${el.title}}
// amount={${el.amount}}
// date={${el.date}}
// ></ExpenseItem>`;
// })
// .join('');
// console.log(html);
return (
<div className="expenses">
<ExpenseItem
title={items[0].title}
amount={items[0].amount}
date={items[0].date}
/>
<ExpenseItem
title={items[1].title}
amount={items[1].amount}
date={items[1].date}
/>
<ExpenseItem
title={items[2].title}
amount={items[2].amount}
date={items[2].date}
/>
<ExpenseItem
title={items[3].title}
amount={items[3].amount}
date={items[3].date}
/>
</div>
);
}
export default Expenses;
So this is the thing. I am hardcoding 4 custom components because that is the length of props.items array.
Code that I commented out will create template literal based on length of items array, so for each item I will have created component. That is all fine but here comes the problem.
I don't know how to append that to the div with class name of "expenses".
That is problem I have.
Thank you in advance for answers.
You can map items array in return and it will render ExpenseItem for all items in array.
Try something like this:-
import ExpenseItem from './ExpenseItem';
import './Expenses.css';
function Expenses(props) {
const { items } = props;
return (
<div className="expenses">
{items.map((item, index) => (
<ExpenseItem
key={item.id || index}
title={item.title}
amount={item.amount}
date={item.date}
/>
))}
</div>
);
}
export default Expenses;
Related
I hope I phrased this question clearly. I have a small recipe app, for the recipe method I want to dynamically add Step 1, Step 2, Step 3 etc. for each step that is passed through via props.
The recipe's steps are passed through as an array of objects:
recipeMethod: Array(2)
0: {step_instructions: 'Boil Water'}
1: {step_instructions: 'Heat Oil'}
length: 2
I am trying to get this array to display as
Step 1
Boil Water
Step 2
Heat Oil
And additionally steps would be added for any further recipe method objects. Currently I can just get the step_instructions to display but cannot get the dynamically incrementing steps (that should start at 1)
Here is the relevant code:
import './MethodStep.css'
import React from 'react'
let methodArray
function mapMethod() {
return methodArray.map((item, idx) => (
<React.Fragment key={idx}>
<div className="method-step small-header"></div>
<div className="method-text">{item.step_instructions}</div>
</React.Fragment>
))
}
function MethodStep(props) {
methodArray = props.recipeMethod || []
return <div className="recipe-method-container">{mapMethod()}</div>
}
export default MethodStep
as #louys mentioned above you can easily achieve that using
Steps {idx + 1 }
Above will print the each index of methodArray after adding 1.
I also noticed you are using Index as key. This is wrong practice as key should be always unique. You can append some string with it to make it unique.
like below:
<React.Fragment key={step-${idx}}>
Here you go, you'll just have to change the initialisation of methodArray to equal props.recipeMethod.
import React from "react";
function mapMethod(methodArray) {
return methodArray.map((item, idx) => (
<React.Fragment key={idx}>
<div className="method-step small-header">Step {idx + 1}</div>
<div className="method-text">{item.step_instructions}</div>
</React.Fragment>
));
}
function MethodStep(props) {
const methodArray = [
{ step_instructions: "Boil Water" },
{ step_instructions: "Heat Oil" },
]; // this is props.recipeMethod;
return (
<div className="recipe-method-container">{mapMethod(methodArray)}</div>
);
}
export default MethodStep;
You need to bind the array to the component state. You could build your own hook handling this for you.
Here's a basic exemple:
function useRecipe(initialRecipe) {
const [recipe, setRecipe] = useState(initialRecipe)
const addStep = step => {
recipe.push(step)
setRecipe(recipe)
}
return [recipe, addStep]
}
function mapMethod(recipe) {
return recipe.map((item, idx) => (
<React.Fragment key={idx}>
<div className="method-step small-header"></div>
<div className="method-text">{item.step_instructions}</div>
</React.Fragment>
))
}
function MethodStep(props) {
const [recipe] = useRecipe(methodArray)
return <div className="recipe-method-container">{mapMethod(recipe)}</div>
}
Just like the title says I'm passing down pokemon data and rickandmorty data. I also happen to be using the tailwind select menu for react thats pretty long. Is there a better way to do it than conditionally map through the data? I know I can do this
{pokemons ? (
{pokemons?.map((pokemon, idx) => (
**30 line long code for the select menu**
))}
) : (
{rickAndMorty?.map((character, idx) => (
**Another 30 long line code for the select menu**
))}
)}
Is this the only way to do it or is there a cleaner way? Any help would be greatly appreciated. Thanks!
I suggest to try and separate any duplicated code out into some generic component, like:
const GenericSelectItem = (props)=>{
return (<>{/* props.itemValues */}</>);
};
const GenericSelectList = (props)=>{
const { selectItems } = props;
return (<SelectList>
{ selectItems.map( selectItem => <GenericSelectItem selectItem={ selectItem } /> ) }
</SelectList>);
};
const Example = (props)=>{
const itemsToDisplay = pokemons || rickAndMorty;
return (<>
{ !itemsToDisplay ? null : <GenericSelectList selectItems={ itemsToDisplay } /> }
</>);
};
In case the SelectItems are very different, add specific components, like:
const PokemonItem = (props)=>{
return (<GenericSelectItem>{/* pokemon specific variations */}</GenericSelectItem>);
};
const RickAndMortyItem = (props)=>{
return (<GenericSelectItem>{/* rickAndMorty specific variations */}</GenericSelectItem>);
};
So I'm making a todo list app and to provide alternate colors to new todo items as they are added in the todolist i have used the if else statement in React component but i think it is not evaluating.
Below are the two classes from my css file that i'm using -->
.bgColorItem1{
background-color: white;
}
.bgColorItem2{
background-color:grey;
}
Below is the component that accepts arguments item(todo item that will be added to the list) and key(index passed as key) from todolist -->
import React from 'react';
import './App.css';
const TodoItem=({item,key})=>{
let settingClass="";
// *****so the problem is here in the if condn that's not putting settingClass as bgColorItem1*****
if(key%2===0){
settingClass="bgColorItem1";
}else{
settingClass="bgColorItem2";
}
return <div className={`boxSpace centerAligning ${settingClass}`}>{item}</div>
}
export default TodoItem;
So what i expect from this code that key which was index in todolist component passed to todo here above should return 0 for even so that settingClass can have alternate values and hence provide alternate colors.But that is not the case.
First of all, don't use key's value since it is internal. Secondly, you can achieve like this
{items.map((item, index) => <TodoItem item={item} index={index} />)}
In TodoItem
const TodoItem=({ item, index })=>{
let settingClass=index % 2 === 0 ? 'bgColorItem1' : 'bgColorItem2';
return <div className={`boxSpace centerAligning ${settingClass}`}>{item}</div>
}
However, you don't need react to do this, just use css, in your css
.boxSpace:nth-child(odd) {
background: red;
}
.boxSpace:nth-child(even) {
background: blue;
}
You can't use key property as it is reserved, also you will get a warning for it:
Warning: ListItem: key is not a prop. Trying to access it will result in undefined being returned. If you need to access the same value within the child component, you should pass it as a different prop.
Simple example:
const ListItem = ({ key }) => {
console.log(key);
return <div>{key}</div>;
};
const App = () => {
return (
<>
{[0, 1, 2, 4].map(item => (
<ListItem key={item} />
))}
</>
);
};
Your TodoItem component shouldn't have to care about the key prop, but rather a boolean (or a string if you have more than 2 styles) prop like alternateStyle:
const TodoItem=({item,alternateStyle})=>{
return <div className={
`boxSpace centerAligning ${alternateStyle ? 'bgColorItem2' :
'bgColorItem1'}`
}>
{item}
</div>
Then you can set the value you need to alternateStyle in the parent component:
<div>
{items.map((item, index) => <TodoItem item={item} alternateStyle={index % 2 !== 0} />)}
</div>
key "prop" is reserved in React. You cannot use it. Rename this props to "idx"
See: Special props
What I want to do, using map, is pretty plain.
I want to call this:
<Striper size={3} text={"Hey everybody!"}/>
To get this:
<>
<Stripe>
<Stripe>
<Stripe>
Hey everybody!
</Stripe>
</Stripe>
</Stripe>
</>
I tried this way, but it fails:
const Striper = (props) => {
const contentTop=props.sizer.map((item)=> <Stripe>)
const contentBottom=props.sizer.map((item)=> </Stripe>)
return (
<div>
{contentTop}
{contentBottom}
</div>
)
}
Basically only this works (which isn't what I want):
const contentTop = props.sizer.map((item)=> <Stripe></Stripe>)
How could I get what I want?
The solution ended up being really simple (thank you, Emile): use .reduce.
As it says in the documentation about reduce, it's really useful when you need only one thing returned. And that's what I needed.
As I said in a comment:
What I want to return from <App size={2} text="Hello"/> is really
<Stripe><Stripe>Hello</Stripe></Stripe>, but because I have to
return a whole object, the closest I can come with map is
<Stripe>Hello</Stripe><Stripe>Hello</Stripe>.
So instead, use reduce.
This is the solution, verified to work. (Note: I'm being somewhat handwavey about size={3} when it's actually an array, because I know how to do that stuff, it isn't my question but you can see my implementation in the answer below).
const Striper = (props) => {
const content = props.sizer.reduce(
(total, currentValue) => <Stripe color={props.colors}>{total}</Stripe>
)
return (
<div>
{content}
</div>
)
}
And as it's actually called:
const arr = Array(6).fill("this is my text!");
return (
<div>
<Striper sizer={arr} colors={colours}/>
</div>
);
I guess you can achieve something like with this approach:
import React from 'react';
import { render } from 'react-dom';
const Stripe = () => <span> – </span>
const App = ({ size, text }) => {
const arrFromNum = Array.from(Array(size), (x, i) => i + 1)
return (
<React.Fragment>
{arrFromNum.map(x => <Stripe />)}
{text}
{arrFromNum.map(x => <Stripe />)}
</React.Fragment>
)
}
render(<App size={4} text="Hello" />, document.getElementById('root'));
Does this answer your question?
Here's one possible way of many different possibilities based on your scenario:
<Striper size={3} text={"Hey everybody!"}/>
export default ({ size, text }) => <><Stripe size={size} /> {text} <Stripe size={size} /></>;
export default ({ size }) => <>{ Array(size).fill().map(_ => <h3>Stripe!</h3>) }</>;
https://stackblitz.com/edit/react-tnqv2k
I am trying to pass props from one component to another. The index property in SortableSectionList does not seemed to be passed to SortableSection though. See console output below.
Index in SortableSectionList: 0
Index in SortableSectionList: 1
(2) Index in SortableSection: undefined
Other properties like menuSection get passed just fine though. See the full code below. Any help is appreciated, thanks!
import React from 'react'
import MenuSection from './MenuSection'
import { SortableContainer, SortableElement } from 'react-sortable-hoc'
class MenuSections extends React.Component {
render() {
const menuSections = this.props.menuSections
const SortableSectionList = SortableContainer(({ menuSections, onSectionSortEnd }) => (
<div>
{menuSections.map(function(menuSection, index) {
console.log('Index in SortableSectionList: ' + index)
return (
<SortableSection
collection="section"
key={`item-${menuSection.id}`}
menuSection={menuSection}
index={index}
menuItems={menuSection.menuItems}
onSortEnd={onSectionSortEnd}
/>
)
})}
</div>
))
const SortableSection = SortableElement(({ menuSection, index, menuItems, onSortEnd }) => {
console.log('Index in SortableSection: '+index)
return (
<MenuSection
key={menuSection.id}
menuSection={menuSection}
index={index}
menuItems={menuItems}
onSortEnd={onSortEnd}
/>
)
})
return (
<div>
<SortableSectionList
menuSections={this.props.menuSections}
lockAxis="y"
lockToContainerEdges
onSortEnd={this.props.onSortEnd}
onSectionSortEnd={this.props.onSectionSortEnd}
/>
</div>
)
}
}
export default MenuSections
It seems that react-sortable-hoc uses index property by itself. So if you want to use it also you better add another property like sortIndex or similar and pass it.
return (
<SortableSection
index={index}
sortIndex={index}
...
They also have an explanation and example in their docs.