I have a div element in which I want to show one item on a click of a button. But every time a user clicks a button, I want him to be able to add another item, and another, etc. Making it a list.
How do I accomplish this with React?
I know I have to separate an item, right?
const Item = () => (
<div>
<h1>Hello</h1>
</div>
)
And then? I'm assuming it's not the same as simply showing/hiding a div with ternary operators? Because the button needs to add +1 every time someone clicks it. Or am I wrong?
Can someone please point me in the right direction because I'm not sure how to search this? Thank you!
Simply put store your item data to state, then use map on that state e.g.
If you use hooks for state.
{items && items.map(item => <Item someProp={item.someValue} />)}
If you have a class based component.
{this.state.items && this.state.items.map(item => <Item someProp={item.someValue} />)}
Note the && is there so react doesn't try to map over a empty state and only maps if the value is truthy aka in this case the state has something in it, if you would try to map over a empty state it would crash react.
Related
I am trying to make an single selection in my app. I have a grid and inside it some images. When I clicked an image its parent which is a div background color change to red. I describe this event it is selected. I want to make deselect previous item when click any where. if clicked image is one of my item then new item should be selected and previous should be unselected. if clicked element is not one of my items just previus should be unselected. When I select an item then clicked one of the others it is working. However when I selected an item then click out of my items it is not working. In this stuation my prevClickedElement parameter refers to previous of previous clicked element. And I have no idea why state empty when update it in clickElement in context and in removePrevSelectedSquare in Piece component. Because of my app has multiple components and images I added to here. I am not sure this is the correct way. Because when I select an item component created items count times in this case 15. This is a performance problem?
I believe a good approach would be creating a component global state to save the current element unique id and creating a function to set the element id when it is clicked. After that you can create a function that reset this state whenever it is called and implement this function as an DOM event in a superior div, the parent of the images. Here is the ideia in form of a code snippet:
import React from 'react';
function Stackoverflow(props) {
const [selectedPicture, setSelectedPicture] = React.useState(null);
function resetSelectedPicture() {
setSelectedPicture(null)
}
return (
<div onClick={resetSelectedPicture}>
{pictures.map((picture, index) => {
const {id, src} = picture;
return (
<>
<img
src={src}
key={index}
id={id}
className={
selectedPicture.id === id
? 'selected-picture'
: 'unselected-picture'
}
onClick={setSelectedPicture(
() => picture
)}
/>
</>
);
})}
</div>
);
}
export default Stackoverflow;
I am trying to build a simple todo list. Basically there is a list of items. When the user clicks the 'edit' button on an item, selectedTodoItem is updated. Then a modal is shown which uses selectedTodoItem as prop.
I am also using ant.design library for the list.
Here is my code (with irrelevant bits left out):
const [selectedTodoItem, setSelectedTodoItem] = useState(selectedTodoList.items[0]);
const editItemHandler = (item) => {
setSelectedTodoItem(item);
dispatch(toggleEditTodoItemModalVisible());
}
return(
<List
className="whitebg listItems"
datasource={selectedTodoList.items}
renderItem={(item) => (
<TodoItemInList item={item} editHandler={editItemHandler} />
)}
/>
<TodoItemEditModal item={selectedTodoItem} />
);
<TodoItemInList> is basically a label and some buttons, one of which calls editHandler(item).
Here's a screenshot so you can get a better idea:
The problem is, when I load the page and click edit (blue, don't mind the delete icon) on an item, say 'Foundation', the edit modal loads fine. But when I click on another blue button, the modal still shows 'Foundation'. No matter what button I click, the modal only shows the first clicked item. It seems like selectedTodoItem is updating though, according to console.log(), but the modal's prop doesn't update with it.
Any nudge in the right direction would be much appreciated.
Can one React component render by 2 classes? Just like I did in the picture.
I tried the above. It gives me another error Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of "Groups".
The button component Im using in Groups method(Groups.jsx) like this way.
const Groups = (props) => (
<div className = 'panel'>
<h2>Groups</h2>
<button >Get Groups</button>
<div className = 'group-list'>
{props.groups.map((group) =>
<GroupsEntry name = {group.name}
members = {group.members}/>
)}
</div>
</div>
);
Do you guys have any idea about this? Thank you
I will try to clarify a little.
You can render a component from whatever parent component you want.
By in the case of your picture, what is telling you that the first component in tree, was App.js, and then App.js rendered Groups.js component, and Groups.js rendered your actual component.
In the same page, the warning you are seeing about using "key" is because you need to set a unique key value for each element that you are rendered as a list, a repeated item. This is because the internal react work to compare if it has to rerender again your component needs it. You will have performance problems (not in an easy example...) if you dont add it. Normally you use the id of the object you are rendering.
I hope i clarified a little.
Yes, a component can be rendered as many times as you would like. The issue is that you are mapping over an array and returning an element. React requires that you put a unique key prop on these elements that ideally are consistent between renders.
You can try to update your code to be something like the following:
const Groups = props => (
<div className="panel">
<h2>Groups</h2>
<button>Get Groups</button>
<div className="group-list">
{props.groups.map(group => (
<GroupsEntry key={group.name} name={group.name} members={group.members} />
))}
</div>
</div>
);
This is assuming group.name is unique. If you have a unique identifier (eg: group.id) that would be ideal.
For more examples and why this is necessary you can checkout the official docs: https://reactjs.org/docs/lists-and-keys.html
I have component with navigation, on click item to child component passed in props some params, one of params - object 'itemImage' with className and url, like this:
{
url: '/static/image.svg',
className: 'absolute hidden md:block min-w-53 lg:min-w-68 mt-30 lg:mt-19 -ml-28 lg:-ml-75',
}
In child component ItemComponent:
{
itemImage &&
<img className={itemImage.className} src={itemImage.url} alt='' />
}
ItemComponent is selected from an array according to the order of the element in navigation (it is responsible for the object passed to the child component), since the list of navigation elements and elements of the array of pictures are not related and of different lengths. The sample is implemented on the basis of an index in map for objects and an index in an array with pictures, to be more precise.
Problem:
the pictures flicker as the state of the parent and the child is updated, is it possible to somehow avoid this and make the change of the picture clear without the flickering of the previous card.
You can use the below-mentioned code to render the Array of Images.
<>
{this.props.Images.map(item=>{
return (item)
})}
<p>
{JSON.stringify(this.state)}
</p>
<p>
{JSON.stringify(this.props.changed)}
</p>
<button onClick={this.props.onChange}>Change</button>
<button onClick={this.onCurrentChange}>Current State Change</button>
</>
Please check the demo here Demo
You can make somethings to try to prevent that.
1- Add a key prop to the elements. It help react understand that it is the same data from before and not re-render that piece.
2- Use react PureComponent on the flickering element https://reactjs.org/docs/react-api.html#reactpurecomponent to prevent the re-render
3 - Instead of purecomponent implement shouldComponentUpdate
EDIT: While Sung Kim's answer is correct for the original scenario, I forgot to add that this behaviour (of selecting the next item from the list) can be toggled by some other key, for instance ArrowDown, in which case the tabIndex would not be of much help initially at least.
`````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````
I'm trying to work on a dropdown component that has two separate (sibling) functional components. The first one is a regular <input> that will take in a value.
The second one is a <ul> that will display the results.
The flow is that initially only the <input> is displayed, when you type in and get results back from the database, then the <ul> gets rendered.
The above functionality is done, however, what I want to accomplish now is that when I'm done typing (because I'm satisfied with the results I see) and I hit tab, then the first item on the results list (precisely a <a> within the <li>) gets focused, and then if I continue to hit tab the next item on the last will focus and so on until it reaches the final item.
So, essentially the focus action could come from either hitting tab on the input or, from the current list item (if it has already been focused).
I've been thinking about the cleanest approach to get this to work. I thought perhaps using ref via createRef and forwardRed could be a good idea, but honestly I can't wrap my head around it for the time being so I thought I'd reach out for help.
This is essentially what it looks like (everything is working as intended, I cut out pretty much all the logic to strip it down to the basics and focus on the main issue here, which is, well, focus...).
Parent Component:
class Parent extends React.Component {
componentDidMount() {}
handleInternalKeyPress = (e) => {
if (e.key === 'Tab') {
e.preventDefault()
// do something?
}
}
render() {
return (
<section>
<section>
<DropdownInput
handleTextChange={this.props.handleTextChange}
handleKeyDown={this.handleInternalKeyPress}
/>
<DropdownResults
results={this.props.results}
handleKeyDown={this.handleInternalKeyPress}
/>
</section>
</section>
)
}
}
Input Component:
const DropdownInput = props => (
<Input
onChange={e => props.handleTextChange(e)}
onKeyDown={e => props.handleKeyDown(e)}
type="text"
/>
)
Results component (<ul>):
// Maybe this should be a React.forwardRef() instead of
// an arrow function, but I'm not sure if this is the
// best/most elegant approach
const DropdownResults = props => (
<ul>
{props.results.map((result, i) => (
<li key={result.resultIdKey}>
<a
// perhaps a ref should go in here?
onKeyDown={e => props.handleKeyDown(e)}
role="link"
tabIndex={i}
>
{result.resultTitleDisplayKey}
</a>
</li>
))}
</ul>
)
Again, the compoenents are quite a bit more complex than this, but this is the basic idea of how they work.
It would also be ideal to get a hold of the focused item to set custom styles to it, for instance.
I've been giving it some thought but this one has really got me, particularly because I want to adhere to best/latest React practices so any help that can be provided will be much appreciated!
I've never used tabIndex but played around after reading some articles.
It looked like setting the tabIndex={0} worked instead of increasing it using i.
const DropdownResults = props => (
<ul>
{props.results.map((result, i) => (
<li key={result.resultIdKey}>
<a
// perhaps a ref should go in here?
onKeyDown={e => props.handleKeyDown(e)}
role="link"
tabIndex={0}
>
{result.resultTitleDisplayKey}
</a>
</li>
))}
</ul>
);
For some reason Google documentation (Using tabindex) says using tabIndex greater than 0 is an anti-pattern without much explanation (as well as this older blog post, which doesn't explain why not either)
even though MDN documentation doesn't say anything about using tabIndex greater than 0 being an anti-pattern.
But for now setting all values of tabIndex=0 seems to work.
You can fork the code.