I'm new to React and programing in general and I'm having trouble writing code that return each element in an array, in React JS.
the whole code is below:
import React from 'react'
const App = () => {
const course = {
name: 'Half Stack application development',
parts: [
{
name: 'Fundamentals of React',
exercises: 10
},
{
name: 'Using props to pass data',
exercises: 7
},
{
name: 'State of a component',
exercises: 14
}
]
}
const Header = (props) => {
return (
<h1>{props.course.name}</h1>
)
}
const Content = (props) => {
const lisItem = props.course.parts.map((part =>
<li>{props.course.parts.name}</li>
))
return (
<ul>{lisItem}</ul>
)
}
return (
<div>
<Header course={course}/>
<Content course={course}/>
</div>
)
}
export default App
Right now it half-works: I can display 3 bullet points (match with numbers of parts) but cannot display the name of the part itself.
Also I would like to clarify the out put a wanted is the course's name and each name of the parts be displayed.
Any help would be appreciated. Thank you very much.
You are not using map correctly. It should be like this:
const lisItem = props.course.parts.map((part) => <li>{part.name}</li>);
You were ignoring each part given to you by map. Check docs of map.
Also I see now you were defining the two components Header and Content inside the App component, that is not good practice (due to reconciliation), move their definition outside of App:
import React from "react";
const Header = (props) => {
return <h1>{props.course.name}</h1>;
};
const Content = (props) => {
const lisItem = props.course.parts.map((part) => <li>{part.name}</li>);
return <ul>{lisItem}</ul>;
};
const App = () => {
const course = {
name: "Half Stack application development",
parts: [
{
name: "Fundamentals of React",
exercises: 10,
},
{
name: "Using props to pass data",
exercises: 7,
},
{
name: "State of a component",
exercises: 14,
},
],
};
return (
<div>
<Header course={course} />
<Content course={course} />
</div>
);
};
Your .map( part => ...) iterates props.course.parts, the part inside the map function is a single item of the list.
Check MDN for more info https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
Related
I am trying to create a simple button multi-select in React but I'm currently getting unexpected behaviour. I'd like users to be able to toggle multiple buttons and have them colourise accordingly, however the buttons seem to act a bit randomly.
I have the following class
export default function App() {
const [value, setValue] = useState([]);
const [listButtons, setListButtons] = useState([]);
const BUTTONS = [
{ id: 123, title: 'button1' },
{ id: 456, title: 'button2' },
{ id: 789, title: 'button3' },
];
const handleButton = (button) => {
if (value.includes(button)) {
setValue(value.filter((el) => el !== button));
} else {
let tmp = value;
tmp.push(button);
setValue(tmp);
}
console.log(value);
};
const buttonList = () => {
setListButtons(
BUTTONS.map((bt) => (
<button
key={bt.id}
onClick={() => handleButton(bt.id)}
className={value.includes(bt.id) ? 'buttonPressed' : 'button'}
>
{bt.title}
</button>
))
);
};
useEffect(() => {
buttonList();
}, [value]);
return (
<div>
<h1>Hello StackBlitz!</h1>
<div>{listButtons}</div>
</div>
);
}
If you select all 3 buttons then select 1 more button the css will change.
I am trying to use these as buttons as toggle switches.
I have an example running #
Stackblitz
Any help is much appreciated.
Thanks
I think that what you want to achieve is way simpler:
You just need to store the current ID of the selected button.
Never store an array of JSX elements inside a state. It is not how react works. Decouple, only store the info. React component is always a consequence of a pattern / data, never a source.
You only need to store the necessary information, aka the button id.
Information that doesn't belong to the state of the component should be moved outside. In this case, BUTTONS shouldn't be inside your <App>.
Working code:
import React, { useState } from 'react';
import './style.css';
const BUTTONS = [
{ id: 123, title: 'button1', selected: false },
{ id: 456, title: 'button2', selected: false },
{ id: 789, title: 'button3', selected: false },
];
export default function App() {
const [buttons, setButtons] = useState(BUTTONS);
const handleButton = (buttonId) => {
const newButtons = buttons.map((btn) => {
if (btn.id !== buttonId) return btn;
btn.selected = !btn.selected;
return btn;
});
setButtons(newButtons);
};
return (
<div>
<h1>Hello StackBlitz!</h1>
<div>
{buttons.map((bt) => (
<button
key={bt.id}
onClick={() => handleButton(bt.id)}
className={bt.selected ? 'buttonPressed' : 'button'}
>
{bt.title}
</button>
))}
</div>
</div>
);
}
I hope it helps.
Edit: the BUTTONS array was modified to add a selected property. Now several buttons can be selected at the same time.
Currently using a react in typescript.
make a separate file and manage it.
I want to do map work on the `card component
parameter on the map wrong?
definition wrong?
Can I get some advice...
Card.tsx
import { LIST_DATA, ListData } from './data';
const Card = () => {
return(
{LIST_DATA.map(({ id, title }: ListData[]) => {
return <S.InfoTitle key={id}>{title}</S.InfoTitle>;
})}
)
}
data.tsx
export interface ListData {
id: number;
title: string;
}
export const LIST_DATA: ListData[] = [
{
id: 0,
title: 'drawing',
},
{
id: 1,
title: 'total',
},
{
id: 2,
title: 'method',
},
{
id: 3,
title: 'material',
},
];
When you map, each item inside the map as argument is only one item from the original array. In your case you have an array of ListData but when you map each argument is only one of this ListData item type, therefore you have to change ListData[] to ListData as such:
import { LIST_DATA, ListData } from './data';
const Card = () => {
return(
{LIST_DATA.map(({ id, title }: ListData) => {
return <S.InfoTitle key={id}>{title}</S.InfoTitle>;
})}
)
}
Otherwise to answer the main question: map in typescript is the same as map in javascript, you just have to ensure you input the right type in order to "secure" your app.
from an array inside an object and renders it to the browser. The App uses a number of small components Header, Contents and Total to function.
Next part of exercise is to create a single component 'Course' to do the same job as all the smaller components(Header,Contents,Total)
Here is original app that gives this output:
Half Stack application development
Fundamentals of React10
Number of exercises is 31
import React from 'react'
const Header = (props) => {
return (
<div>
<h1>{props.course}</h1>
</div>
)
}
const Content = (props) => {
return (
<div>
<p>{props.part1}</p>
</div>
)
}
const Part = (props) => {
return (
<div>
<p>{props.part1} {props.exercises1}</p>
</div>
)
}
const Total = (props) => {
return (
<div>
<p>Number of exercises is {props.total}</p>
</div>
)
}
const App = () => {
const course = {
name: 'Half Stack application development',
parts: [
{
name: 'Fundamentals of React',
exercises: 10
},
{
name: 'Using props to pass data',
exercises: 7
},
{
name: 'State of a component',
exercises: 14
}
]
}
return (
<div>
<Header course={course.name} />
<Content part1 = {course.parts[0].name + course.parts[0].exercises} />
<Total total = {course.parts[0].exercises + course.parts[1].exercises + course.parts[2].exercises} />
</div>
)
}
export default App
And here is the new component I created which is giving me a blank output, no errors reported
const Course = (props) => {
return (
<div>
<Header></Header>
<Content></Content>
<Total></Total>
</div>
)
}
With updated App
onst App = () => {
const course = {
name: 'Half Stack application development',
parts: [
{
name: 'Fundamentals of React',
exercises: 10
},
{
name: 'Using props to pass data',
exercises: 7
},
{
name: 'State of a component',
exercises: 14
}
]
}
return <Course course={course} />
}
export default App
I created a CodeSandbox so I can elaborate my question.
I would like to ask for your suggestion on my Project:
I currently have a website portfolio app that are divided into 4 pages:
Loading.js directly fetch -> Home.js
About.js
Contact.js
Work.js – it displays a link of my projects that will open a Sliding Sidebar/Side Drawer
feature.
What I wanted to do is to fetch the individual project components and pass it in the Sliding Sidebar once a specific project was clicked by the user.
My question is what is the best way to manage the state? how do I pass the props from the project that was clicked and display the specific project component from the components folder?
CodeSandbox Link <----
updated work.js
import React, { useState } from "react";
import StyledWorkNav from "./StyledWorkNav";
import SideDrawer, { StyledDrawer } from "./SideDrawer";
import Project1 from "./components/Project1";
import Project2 from "./components/Project2";
import Project3 from "./components/Project3";
const Work = () => {
const [drawerOpen, setDrawerOpen] = useState(false);
const [projects, setProjects] = useState([
{ name: 'Project 1', projId: '1', dataText: 'Proj 1', comp:"" },
{ name: 'Project 2', projId: '2', dataText: 'Proj 2', comp:"Project2" },
{ name: 'Project 3', projId: '3', dataText: 'Proj 3', comp:"Project3" },
]);
const [selectedProject, setSelectedProject] = useState(null);
const strToComponent = {
Project1: <Project1/>,
Project2: <Project2/>,
Project3: <Project3/>
}
const openDrawerHandler = () => {
if (!drawerOpen) {
setDrawerOpen(true);
} else {
setDrawerOpen(false);
}
};
const closeDrawerHandler = () => {
setDrawerOpen(false);
};
// -------------------****** update **************
let drawer;
if (drawerOpen) {
drawer = (
<SideDrawer
close={closeDrawerHandler}
sidebar={{ StyledDrawer }}
// pass down here one of the wanted component : project1.js, 2 etc..
project={
<Project1
selectedProject={selectedProject} // you can pass the selected
// project as prop for
// project1.js for example
/>
}
/>
);
}
return (
<StyledWorkNav>
<ul>
{projects.map((project) => (
<li
key={project.projId}
onClick={() => {
setSelectedProject(project);
openDrawerHandler();
}}>
<p data-text={project.dataText}>{project.name}</p>
</li>
))}
{selectedProject && drawer}
</ul>
</StyledWorkNav>
);
};
export default Work;
you can do something like this :
Upadate
imports ......
// this state will contain all your projects
const [projects, setProjects] = useState([
{
id: 1,
name: "project1"
},
{
id: 2,
name: "project2"
},
.....
])
// this will contain on of the project selected from the list of
// projects
const [selectedProject, setSelectedProject] = useState({
id: 1,
name: "project1"
})
return (
<>
<List>
{ projects.map(project => (
<ListItem key={project.id} onClick={() => setSelectedProject(project)}>
{project.name}
</ListItem>
))
}
</List>
{
selectedProject &&
<Sidebar // the selected project goes here and change every time a different project selected
project={selectedProject}
/>
}
</>
)
export ......
I'm new to Hooks and trying to create a PureComponent equivalent version with Hooks.
My goal is to create a multiselectable list with a child component that is reponsible for rendering the list items:
const Movie: FunctionComponent<{
title: string,
score: number,
id: number,
isSelected: boolean,
setSelected: React.Dispatch<React.SetStateAction<{}>>
}> = React.memo(({ title, score, isSelected, setSelected, id }) => {
const selectMovie = (): void => {
if (isSelected === null)
return;
setSelected(id);
};
const selected = {
backgroundColor: "blue"
}
console.log("render movie")
return (
<div onClick={selectMovie} style={isSelected ? selected : {}}>
{title}, {score}
</div>
)
})
The parent component have the data as well as the logic for the selection:
const App: FunctionComponent = () => {
const data = [
{
id: 1,
title: "Pulp fiction",
score: 9
},
{
id: 2,
title: "Heat",
score: 8
},
{
id: 3,
title: "American pie",
score: 7
}
]
const [selectedItems, setSelected] = React.useState<{}>({});
const selectMovie = React.useCallback((id: any) => {
const sel: any = {...selectedItems};
if (sel.hasOwnProperty(id)) {
delete sel[id]
} else {
sel[id] = true;
}
setSelected(sel);
}, [selectedItems])
return (
<div>
{
data.map(e => <Movie key={e.id} {...e} setSelected={selectMovie} isSelected={selectedItems.hasOwnProperty(e.id)}/>)
}
</div>
)
}
I made a sandbox where you can try it out: https://codesandbox.io/s/old-sun-38n4u
The selection state is maintained in the parent component in an object and supplied to the child as a boolean. The problem is when I click on a movie all 3 list items re-renders (you can see the log in the console). I have used React.memo and useCallback as well to avoid arrow function re-creation for the props comparison. I'm pretty new to hooks, so it must be something silly I'm overlooking...
That is because your selectMovie is changing every time due to selectedItems dependency changing.
setSelected function can also take a function and you can get the selectedItems value so you don't need to set it as a dependency
Here is the working sandbox