How do I compose a better React list container component? - javascript

I have a small app fetching Movies. The component tree isn't very deep. I have state in App.js and it passes data to the Movies.js component. Now, Movies.js is just a container to render the ul list of Movies.
App.js passing data to Movies
<Movies
modalData={this.state.modalData}
toggleModal={this.openModal}
isOpen={this.state.modalIsOpen}
closeModal={this.closeModal}
items={this.state.data}
getMovieInfo={this.getMovieInfo}
addToFavorites={this.addToFavorites}
/>
Movies.js
const Movies = props => {
const { items, getMovieInfo, addToFavorites, isOpen, toggleModal, closeModal, modalData } = props;
const listItems = items.map((item, id) => (
<Movie
key={id}
id={id}
imdbID={item.imdbID}
title={item.Title}
poster={item.Poster}
getMovieInfo={getMovieInfo}
addToFavorites={addToFavorites}
imdbid={item.imdbID}
isOpen={isOpen}
toggleModal={toggleModal}
closeModal={closeModal}
modalData={modalData}
/>
));
return <ul id="movie-container">{listItems}</ul>;
};
and finally Movies.js which is just an item that is rendered in the Movie component. I tried using destructuring to keep the items prop for the Movies component because it needs that to map over and then ...props to Movie. I cant seem to make this work.
My app works fine but this is 'prop-drilling' and I think context may not be appropriate because this is not a deep tree. Higher Order Components, I don't understand yet. What can I do to make this better?
Movie.js
const Movie = props => {
const {
id,
imdbID,
title,
poster,
getMovieInfo,
addToFavorites,
isOpen,
toggleModal,
closeModal,
modalData,
} = props;
return (
<React.Fragment>
<li className="movie" id={id}>
<img srcSet={poster} alt={title} imdbid={imdbID} />
<div className="movie-title">{title}</div>
<div className="button-container">
<button
type="submit"
className="button-small"
onClick={toggleModal}
imdbid={imdbID}
title={title}
id={id}>
{' '}
Info!!
</button>
<button
type="submit"
className="button-small"
onClick={addToFavorites}
imdbid={imdbID}
id={id}>
{' '}
Fav
</button>
</div>
</li>
<Modal className="modal" selector="#modal-root" isOpen={isOpen} onClick={closeModal}>
<div className="modal-1">

You can do like:
const Movies = ({ items, ...other}) => {
<Movie {...other} />
Or,
const { items, ...other } = props
<Movie {...other} />
Your code in example:
const Movies = ({ items, ...other}) => {
const listItems = items.map((item, id) => (
<Movie
key={id}
id={id}
{...other}
/>
));
return <ul id="movie-container">{listItems}</ul>;
};
Your update made me afraid. You need to destructure all otherwise it will go even longer like id={other.id}. If you feel it's good rather, then do:
const { items, ...other } = props
// now when you use other props like imdbid
imdbid={other.imdbid}

Related

Questions about antd's unique key and component structure

I'm working on a project using react, next.js, antd.
However, the following error occurred:
Warning: Each child in a list should have a unique "key" prop.
We know that the following error occurs because the list's child element does not have a unique key.
So, I tried to fix the code where the error occurred by using the following methods, but it was not resolved.
const BoardList = () => {
const { boardPosts } = useSelector((state) => state.user);
return (
<section>
<List
itemLayout="vertical"
bordered
size="large"
pagination={{
onChange: (page) => console.log(page), pageSize: 3,
}}
dataSource={boardPosts}
renderItem={(item) => (
<List.Item key={item.id}>
<BoardCard post={item} />
</List.Item>
)}
/>
</section>
)
};
------------------------------
try 0.
<List.Item>
<BoardCard post={item} key={item.id} />
</List.Item>
try 1.
<List.Item key={item.id}>
<BoardCard post={item} />
</List.Item>
try 2.
renderItem={(item, i) => (
<List.Item key={i}>
<BoardCard post={item} />
</List.Item>
try 3.
<List.Item>
<div key={item.id}>
<BoardCard post={item} />
</div>
</List.Item>
try 4.
<div key={item.id}>
<List.Item>
<BoardCard post={item} />
</List.Item>
</div>
So I start to doubt whether the above problem is a problem with the component structure.
The component where the problem occurred consists of the following three components.
// pages/profile
import React from 'react';
import NicknameEditForm from './NicknameEditForm';
import MyScrap from './MyScrap';
import MyBoard from './MyBoard';
const MyInfo = () => {
return (
<section>
<NicknameEditForm /> // A component that changes a member's nickname
<MyScrap /> // Gets the post that has been liked from the POST table.
<MyBoard /> // I get the post I wrote from the POST table.
</section>
)
};
export default MyInfo;
// MyScrap.js
const { likedPosts } = useSelector((state) => state.post);
// MyBoard.js
const { boardPosts } = useSelector((state) => state.post);
Also, MyScrap and MyBoard components receive the following data from the backend and use it.
// likedPosts used in MyScrap component
likedPosts = [{id: 1, title: 'title1',.....}, [...] ...]
// boardPosts used in MyBoard component
boardPosts = [{id: 1, title: 'title1',.....}, [...] ...]
So, I wonder if the following problem occurred because the key values of MyScrap component and MyBoard component overlap due to the following structure on one page or if there is another reason.
Try using rowKey in List component. You can check the example here
const BoardList = () => {
const { boardPosts } = useSelector((state) => state.user);
return (
<section>
<List
itemLayout="vertical"
bordered
size="large"
rowKey={(item) => item.id}
pagination={{
onChange: (page) => console.log(page), pageSize: 3,
}}
dataSource={boardPosts}
renderItem={(item) => (
<List.Item>
<BoardCard post={item} />
</List.Item>
)}
/>
</section>
)
};

<li> elements missing keys even after assigning unique keys to each element (ReactJS) [duplicate]

I am a beginner at React. I wrote this code and got an Error
import { DoItem } from '../MyComponents/DoItem'
export const ToDo = (props) => {
return (
<div className="container">
<h3 className="text-center">To Do List</h3>
{props.todos.map((todo) => {
return (
<>
<DoItem const todo={todo} onDelete = {props.onDelete} />
<hr />
</>
)
})}
</div>
)
}
Here's the error Each child in a list should have a unique "key" prop.
I looked online and found that I have to use a key.
I inserted the key
<DoItem const todo={todo} key={todo.nos} onDelete = {props.onDelete} />
But the error still didn't go after I reload the page.
Please add a unique key to every item
{props.todos.map((todo, index) => {
return (
<div key={index}>
<DoItem const todo={todo} onDelete = {props.onDelete} />
<hr />
</div>
)
})}
Please have a try and let me know if it works or not.
import { DoItem } from '../MyComponents/DoItem'
export const ToDo = (props) => {
return (
<div className="container">
<h3 className="text-center">To Do List</h3>
{props.todos.map((todo, i) => {
return (
<key={i}>
<DoItem const todo={todo} onDelete = {props.onDelete} />
<hr />
</>
)
})}
</div>
)
}
Yeah, you should add a key prop to anytime you're rendering an Array.
import { DoItem } from '../MyComponents/DoItem'
export const ToDo = (props) => {
return (
<div className="container">
<h3 className="text-center">To Do List</h3>
{props.todos.map((todo,index) => {
return (
<div key={index | the id of the todo}>
<DoItem const todo={todo} onDelete = {props.onDelete} />
<hr />
</div>
)
})}
</div>
)
}

React Error: Each child in a list should have a unique "key" prop

I am a beginner at React. I wrote this code and got an Error
import { DoItem } from '../MyComponents/DoItem'
export const ToDo = (props) => {
return (
<div className="container">
<h3 className="text-center">To Do List</h3>
{props.todos.map((todo) => {
return (
<>
<DoItem const todo={todo} onDelete = {props.onDelete} />
<hr />
</>
)
})}
</div>
)
}
Here's the error Each child in a list should have a unique "key" prop.
I looked online and found that I have to use a key.
I inserted the key
<DoItem const todo={todo} key={todo.nos} onDelete = {props.onDelete} />
But the error still didn't go after I reload the page.
Please add a unique key to every item
{props.todos.map((todo, index) => {
return (
<div key={index}>
<DoItem const todo={todo} onDelete = {props.onDelete} />
<hr />
</div>
)
})}
Please have a try and let me know if it works or not.
import { DoItem } from '../MyComponents/DoItem'
export const ToDo = (props) => {
return (
<div className="container">
<h3 className="text-center">To Do List</h3>
{props.todos.map((todo, i) => {
return (
<key={i}>
<DoItem const todo={todo} onDelete = {props.onDelete} />
<hr />
</>
)
})}
</div>
)
}
Yeah, you should add a key prop to anytime you're rendering an Array.
import { DoItem } from '../MyComponents/DoItem'
export const ToDo = (props) => {
return (
<div className="container">
<h3 className="text-center">To Do List</h3>
{props.todos.map((todo,index) => {
return (
<div key={index | the id of the todo}>
<DoItem const todo={todo} onDelete = {props.onDelete} />
<hr />
</div>
)
})}
</div>
)
}

unable to access custom component property from parent component

I have created one custom component, alternative of Select component, on click shows ul and on click hide ul.
when I click I can set value to the state inside function, but i want to access the value in parent component.
so my component is
const [showMenu, setShowMenu] = useState(false);
const [value, setValue] = useState();
return (
<>
<button
className='btn'
onClick={() =>
showMenu ? setShowMenu(false) : setShowMenu(true)
}>
{props.name}
</button>
<ul className={showMenu ? "" : "hide"}>
{props.listsa.map((element) => {
return (
<li
key={element.key}
value={element.value}
onClick={(e) => {
setValue(e.target.value);
}}>
{element.label}
</li>
);
})}
</ul>
</>
I want to access value mentioned above functional component in parent component, which is my app.js
as shown below, this is return method of parent component.
<div className='App'>
{/* <Main /> */}
<OptionsComponent name='ABC Menu' listsa={abc} />
{/* here I want to use that value to perfom operations/ also on change it should show changed value */}
</div>
I tried using localStorage.setItem("value":value) it works but that will use browser memory so I am looking for alternative way.
I tried exporting variable, it shows undefined, also I tried making varibale global, it works but doesnt reflect change.
any help will be appreciated
You just need to bring the state up and pass it down, instead:
const [value, setValue] = useState();
return (
<div className='App'>
{/* <Main /> */}
<OptionsComponent name='ABC Menu' listsa={abc} value={value} setValue={setValue}/>
</div>
And
const [showMenu, setShowMenu] = useState(false);
return (
<>
<button
className='btn'
onClick={() =>
showMenu ? setShowMenu(false) : setShowMenu(true)
}>
{props.name}
</button>
<ul className={showMenu ? "" : "hide"}>
{props.listsa.map((element) => {
return (
<li
key={element.key}
value={element.value}
onClick={(e) => {
props.setValue(e.target.value);
}}>
{element.label}
</li>
);
})}
</ul>
</>
);

ReactJS Function Component: accordion, allow for multiple selected open with useState

I've mocked a simple logic for an accordion collapsible panels in ReactJS. I'm trying to allow for multiple collapsible to be open but I'm not able to avoid all the collapsible to open and close at once no matter which collapsible has been clicked. This below is the logic for the accordion to allow only one collapsible at the time.
//Accordion.js
import React, { useState } from "react";
import styled, { css } from "styled-components";
import PropTypes from "prop-types";
import Collapse from "./Collapse";
import Header from "./Header";
const Accordion = ({ list, icon}) => {
const [isActiveIndex, setActiveIndex] = useState(null);
const toggleItem = index => {
setActiveIndex(isActiveIndex === index ? null : index);
};
return (
<Wrapper>
{list.map((item, index) => {
const checkOpen = isActiveIndex === index;
return (
<Container key={index}>
<Header
title={item.title}
icon={icon}
id={index}
onClick={toggleItem}
/>
<Body isOpen={checkOpen}>
<Collapse isOpen={checkOpen}>{item.content}</Collapse>
</Body>
</Container>
);
})}
</Wrapper>
);
};
I've created the whole mock in CodeSandBox here: https://codesandbox.io/s/1r2mvk87q
For the initial accordion I'm using useState and checking for the active index - for the allow multiple I guess I should check the previous state of the clicked item but I'm not able to pass the clicked item as the only target for the state to be checked.
//AccordionMultiple.js
const AccordionM = ({ list, icon }) => {
const [isOpen, setOpen] = useState(false);
const toggleItemM = index => {
setOpen(prevState => !prevState);
};
return (
<Wrapper>
{list.map((item, index) => {
return (
<Container key={index}>
<Header
title={item.title}
icon={icon}
id={index}
onClick={toggleItemM}
/>
<Body isOpen={isOpen}>
<Collapse isOpen={isOpen}>{item.content}</Collapse>
</Body>
</Container>
);
})}
</Wrapper>
);
};
In order to allow for multiple collapsible column, you can make use of an object instead of a single index
const Accordion = ({ list, icon}) => {
const [isActivePanel, setActivePanel] = useState({});
const toggleItem = index => {
setActivePanel(prevState => ({...prevState, [index]: !Boolean(prevState[index])}));
};
return (
<Wrapper>
{list.map((item, index) => {
const checkOpen = isActivePanel[index];
return (
<Container key={index}>
<Header
title={item.title}
icon={icon}
id={index}
onClick={toggleItem}
/>
<Body isOpen={checkOpen}>
<Collapse isOpen={checkOpen}>{item.content}</Collapse>
</Body>
</Container>
);
})}
</Wrapper>
);
};

Categories