How to get previous clicked element on any click event in Reactjs? - reactjs

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;

Related

I need help to selecting a button from an array of buttons using material ui

So things are like this, I have an array of phone numbers, it's mapped so everynumber has a button to copy the number and make a call. Thing is, we use material-ui, specifically the v4 of it.
What i need to do is that whenever someone clicks a button this button change it's color to idk green let's say so the worker knows they are using that button, avoiding clicking again and those kind of things.
material-ui has it's own way to put a color to the button using the color prop and the createTheme API, is there ANY way to do this using &:active or &:selected in material with the createTheme API, is there a way to do it without it (it's not mandatory to use createTheme).
Any help is appreciated, thanks!
If you are rendering your buttons using a map, store the index of the button that is clicked in some state, and use it to compare the index of the current button with that stored index, if matches change the color
something like:
export default function App() {
const [active, setActive] = useState(null);
return (
<div className="App">
{buttons.map((button, idx) => {
const isActive = active === idx;
return (
<Button
key={`btn-${idx}`}
onClick={() => setActive(idx)}
variant="contained"
color={isActive ? "success" : "primary"}
>
{button}
</Button>
);
})}
</div>
);
}

useState set method for modal not updating

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.

React widgets combobox; how to clear input or prevent selection

I'm using the combobox from React Widgets as a search UI component.
I've put in a custom render item so that when you click a search result in the dropdown, you navigate to the relevant page.
However when you select a result, the name of the selected item goes into the text input, which isn't what a user will expect when they select a search result. I think they'd expect the search term to remain, or perhaps the input to be cleared.
I like the Combobox component and haven't found another UI widget that would do what I want, so I'd like to find a solution.
Is there some way to override the selection behaviour so that clicking a list item doesn't select it? I've tried setting the 'onSelect' property but this doesn't suppress the default selection behaviour, it just adds extra functionality.
Alternatively is there a way to manually set the selection to null? The docs don't seem to show anything. I tried getting the input node's value manually to '' with reactDOM, but the value didn't change. I would guess that the component controls it.
I've wrapped the Combobox in a functional component:
function Search(props) {
...
const onSelect = (value) => {
const node = ReactDOM.findDOMNode(Search._combobox);
const input = node.getElementsByTagName('input')[0];
input.value = '';
}
return (
<Combobox
ref={(c) => Search._combobox = c}
onSelect={onSelect}
textField="name"
valueField="_id"
/>
);
}
If I set the value prop of the Combobox then it is impossible to type into it.
Any suggestions? Thank you.
The solution I found is to create my own search controls using an input and a button, and hide the native input and button with display: none. "componentDidUpdate" detects when new search results arrive and opens the dropdown to show them.
There is a manually-added 'show more...' entry at the end of search results. Clicking this increases the search limit for that group. That's the main reason I wanted to avoid showing the clicked result in the text input. The custom input is not affected by the user's selection, it always shows the search term.
My search component now looks something like this:
<div className="search">
<div className="search-controls">
<Input
onChange={this.onChangeInput}
type="text"
/>
<Button
onClick={this.toggleOpen}
title="toggle results"
>
<FontAwesomeIcon icon={['fas', 'search']} style={{ 'color': iconColors.default }} size="1x" />
</Button>
</div>
<Combobox
busy={isSearching}
data={searchResults}
onChange={() => {}}
open={open}
onSelect={this.onSelect}
textField="name"
valueField="_id"
/>
</div>

Adding items to a div with React

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.

Add elements to Blueprint Select component popover?

Blueprint's Select component is exactly what I need for my current React project, with one exception: I need to add some elements to its popover and don't see any way to do it.
Specifically, I'd like to add a title (e.g. an H2 element) above the filter input, and a bar of buttons (e.g., some Button components in a DIV) below the list. Select seems highly configurable but I see no way to add elements inside the popover...what am I missing?
If you want to extend the select's Menu and add custom elements to it, then you have to provide the itemListRenderer prop.
Here's what the docs says:
By default, Select renders the displayed items in a Menu. This
behavior can be overridden by providing the itemListRenderer prop,
giving you full control over the layout of the items. For example, you
can group items under a common heading, or render large data sets
using react-virtualized.
itemListRenderer example:
If provided, the itemListRenderer prop will be called to render the contents of the dropdown menu. It has access to the items, the current query, and a renderItem callback for rendering a single item. A ref handler (itemsParentRef) is given as well; it should be attached to the parent element of the rendered menu items so that the currently selected item can be scrolled into view automatically.
Therefore in the Menu component's body you can place your custom headings and buttons:
import { ItemListRenderer } from "#blueprintjs/select";
const renderMenu: ItemListRenderer<Film> = ({ items, itemsParentRef, query, renderItem }) => {
const renderedItems = items.map(renderItem).filter(item => item != null);
return (
<Menu ulRef={itemsParentRef}>
<h2>Your heading can be styled here</h2>
<MenuItem
disabled={true}
text={`Found ${renderedItems.length} items matching "${query}"`}
/>
{renderedItems}
<div>
<button>Button name</button>
</div>
</Menu>
);
};
<FilmSelect
itemListRenderer={renderMenu}
itemPredicate={filterFilm}
itemRenderer={renderFilm}
items={...}
onItemSelect={...}
/>
Jordan's suggestions above, plus a little experimentation, ultimately yielded a workable answer:
Set filterable to false to hide the built-in filter input.
Use itemListRenderer to render not only the dropdown items, but also an InputGroup to serve as a replacement filter.
Use InputGroup's inputRef prop to capture a ref to the underlying HTML input. Use that to focus the input when it appears, via the onOpening property of Select's popoverProps prop.
Here's a simple component implementing the above:
// Extends Blueprint's Select component with header and footer props that
// can be any arbitrary elements or components
class ExtendedSelect extends Component {
constructor(props) {
super(props);
this.inputRef = null;
this.state = {query: ""};
}
handleInputChanged = event => {
this.setState({query: event.target.value});
}
receiveInputRef = (ref) => {
this.inputRef = ref;
}
handlePopoverOpening = () => {
if (this.inputRef) {
this.inputRef.focus();
}
}
listRenderer = ({filteredItems, renderItem}) => {
// Apply the supplied item renderer to the filtered list of items
const renderedItems = filteredItems.map(renderItem);
return (
<div>
{this.props.header}
<InputGroup inputRef={this.receiveInputRef} value={this.state.query} onChange={this.handleInputChanged} leftIcon="search" />
<Menu>
{renderedItems}
</Menu>
{this.props.footer}
</div>
);
}
render() {
return (
<Select
items={this.props.items}
filterable={false}
query={this.state.query}
itemListRenderer={this.listRenderer}
itemPredicate={this.props.itemPredicate}
itemRenderer={this.props.itemRenderer}
popoverProps={{onOpening:this.handlePopoverOpening}}
onItemSelect={this.props.onItemSelect}
>
{this.props.children}
</Select>
);
}
}
(Note that I'm only passing some of Select's props into the custom component—I suspect I'd know a way to pass them all were I a more experienced React developer.)
This works surprisingly well—e.g., other than a little work to focus the input when it appears, all of Select's other built-in behavior works as expected, like keyboard navigation of the menu while the input is focused.

Resources