React Select with avatar class component whats wrong here select not populating - reactjs

Hi I had working select box with avatar on functional aporx. But I need build in functions to change selectbox properties like hidden: true/false, update option data function to add new option data to display selectbox with certain values on fly.
What I wrong here? Render part works as tested with functional version. The class factoring misses something as select box does not get info ether options and avatar to display and no width calculation happening.
Orginal functional code: https://codesandbox.io/s/react-select-longest-option-width-geunu?file=/src/App.js
Class based works but nodatin selectbox. Here is app.js with select.js fiddle: https://codesandbox.io/s/react-select-longest-option-width-forked-plqq0p?file=/src/Select.js
Source:
import React, { useRef } from "react";
import Select from "react-select";
class RtSelect extends React.Component {
constructor(props) {
super();
this.state = {
info: props.info,
options: props.options,
hidden: props.hidden,
menuIsOpen: false,
menuWidth: "",
IsCalculatingWidth: false
};
this.selectRef = React.createRef();
this.onMenuOpen = this.onMenuOpen.bind(this);
}
componentDidMount() {
if (!this.menuWidth && !this.isCalculatingWidth) {
setTimeout(() => {
this.setState({IsCalculatingWidth: true});
// setIsOpen doesn't trigger onOpenMenu, so calling internal method
this.selectRef.current.select.openMenu();
this.setState({MenuIsOpen: true});
}, 1);
}
}
onMenuOpen() {
if (!this.menuWidth && this.isCalculatingWidth) {
setTimeout(() => {
const width = this.selectRef.current.select.menuListRef.getBoundingClientRect()
.width;
this.setState({menuWidth: width});
this.setState({IsCalculatingWidth: false});
// setting isMenuOpen to undefined and closing menu
this.selectRef.current.select.onMenuClose();
this.setState({MenuIsOpen: true});
}, 1);
}
}
styles = {
menu: (css) => ({
...css,
width: "auto",
...(this.isCalculatingWidth && { height: 0, visibility: "hidden" })
}),
control: (css) => ({ ...css, display: "inline-flex " }),
valueContainer: (css) => ({
...css,
...(this.menuWidth && { width: this.menuWidth })
})
};
setData (props) {
this.setState({
info: props.info,
options: props.options,
hidden: props.hidden
})
}
render() {
return (
<div style={{ display: "flex" }}>
<div style={{ margin: "8px" }}>{this.info}</div>
<div>
<Select
ref={this.selectRef}
onMenuOpen={this.onMenuOpen}
options={this.options}
menuIsOpen={this.menuIsOpen}
styles={this.styles}
isDisabled={this.hidden}
formatOptionLabel={(options) => (
<div className="select-option" style={{ display: "flex" }}>
<div style={{ display: "inline", verticalAlign: "center" }}>
<img src={options.avatar} width="30px" alt="Avatar" />
</div>
<div style={{ display: "inline", marginLeft: "10px" }}>
<span>{options.label}</span>
</div>
</div>
)}
/>
</div>
</div>
);
}
}
export default RtSelect;

Got it working!
I had removed "undefined" from onOpen setState function. I compared those 2 fiddles and finally got it working.
// setting isMenuOpen to undefined and closing menu
this.selectRef.current.select.onMenuClose();
this.setState({MenuIsOpen: undefined});

Related

Google Map info window not loading - React

I'm very new to React, but I've managed to build a functioning Google Map component that is successfully centering in a location derived from a search and populating the map with results from Json results. However, I can't get an Info Window to show when clicking one of the map markers and I don't know where I'm going wrong.
Here's the code for the Map Marker and the Info Window:
const ReactMapComponent = ({ text }) => (<div style={{ background: 'url(/olb/images/mapMarker.png)', height: '44px', width: '35px', cursor: 'pointer' }}>{}</div>);
const InfoWindow = ({ text }) => (<div style={{ background: '#fff', height: '100px', width: '135px', cursor: 'pointer', position: 'relative', zIndex: 100 }}>{}</div>);
class VenueMapChild extends React.Component {
constructor(props) {
super(props);
this.state = {
isOpen: false
}
}
handleToggleOpen = () => {
this.setState({
isOpen: true
});
};
handleToggleClose = () => {
this.setState({
isOpen: false,
});
};
render() {
return (
<ReactMapComponent
key={this.props.key}
lat={this.props.lat}
lng={this.props.lng}
text={this.props.hotelName}
onClick={() => this.handleToggleOpen()}
>
{this.state.isOpen &&
<InfoWindow onCloseClick={() => this.handleToggleClose()}>
<span>Something</span>
</InfoWindow>
}
</ReactMapComponent>
);
}
}
Thanks in advance

React change state for all elements except for the chosen one

I have a bunch of buttons that change style on events such as hover, click etc.
I want to do a simple operation with onClick event to highlight the pressed button.
It's doing well but then other buttons need to get rid of the class that highlights them ('light-active' and 'sect-active'). These classes don't go away because the state isActive is true as the button was pressed before.
I can't come up with a solution.
Here's my code: (please note that I refer to buttons as 'pages')
App.js - Root
import React, { Component } from 'react';
import PageButtons from './components/PageButtons';
import { v4 as uuidv4 } from 'uuid';
import PageBody from './components/PageBody';
class App extends Component {
state = {
pages: [
{
id: uuidv4(),
title: 'Body',
active: false,
},
{
id: uuidv4(),
title: 'Parents',
active: false,
},
{
id: uuidv4(),
title: 'Head',
active: false,
},
{
id: uuidv4(),
title: 'Face',
active: false,
},
{
id: uuidv4(),
title: 'Hair',
active: false,
},
{
id: uuidv4(),
title: 'Accessories',
active: false,
},
],
};
render() {
return (
<div className='App'>
<div id='container' style={cont}>
<PageButtons pages={this.state.pages} />
<PageBody />
</div>
</div>
);
}
}
const cont = {
padding: '1em',
width: '40vw',
display: 'grid',
placeContent: 'center',
};
export default App;
PageButtons.js - Parent component for buttons
import React, { Component } from 'react';
import PageButton from './PageButton';
import PropTypes from 'prop-types';
class PageButtons extends Component {
render() {
return (
<div style={pagesStyle}>
{this.props.pages.map((page) => (
<PageButton
key={page.id}
page={page}
/>
))}
</div>
);
}
}
const pagesStyle = {
alignSelf: 'end',
display: 'flex',
flexWrap: 'nowrap',
overflowX: 'auto',
overflowY: 'hidden',
alignItems: 'center',
justifyContent: 'space-between',
width: '30vw',
marginBottom: '1vw',
cursor: 'pointer',
padding: '1.3vw',
};
// PropTypes
PageButtons.propTypes = {
pages: PropTypes.array.isRequired,
};
export default PageButtons;
PageButton.js - the buttons
import React, { Component } from 'react';
class PageButton extends Component {
state = {
isHovered: false,
isActive: false,
};
handleHover = () => {
this.setState({ isHovered: !this.state.isHovered });
};
handleMouseDown = () => {
this.setState({ isActive: !this.state.isActive });
};
render() {
const isHovered = this.state.isHovered;
const isActive = this.state.isActive;
const { id, title } = this.props.page;
return (
<div
id='singlePageBtnStyle'
className='page'
onMouseEnter={this.handleHover}
onMouseLeave={this.handleHover}
onMouseDown={this.handleMouseDown}
>
<div
className={`light ${
isHovered ? 'light-2' : [isActive ? 'light-active' : 'light']
}`}
></div>
<div
className={`sect ${
isHovered ? 'sect-2' : [isActive ? 'sect-active' : 'sect']
}`}
>
<p
dataText={title}
style={{ margin: 'auto' }}
className={`${isHovered ? 'glitch' : ''}`}
>
{title}
</p>
</div>
</div>
);
}
}
export default PageButton;
Since you want only a single button to be active at-a-time, IMO it make more sense to use a separate slice of state to hold an active id/index/etc... (The alternative would be to shallow copy the entire pages array just to toggle false all the active properties and the 1 single true active property)
Remove the active property from your App state. The state here should just drive the content for the menu items.
state = {
pages: [
{
id: uuidv4(),
title: 'Body',
},
{
id: uuidv4(),
title: 'Parents',
},
...
{
id: uuidv4(),
title: 'Accessories',
},
],
};
Let PageButtons hold the active page/button state. Create a toggle handler and pass as an onClick handler to PageButton along with a computed isActive prop.
class PageButtons extends Component {
state = {
activeId: null,
}
// NOTE: Curried function to consume id and return onClick handler function
toggleActive = id => e => this.setState(({ activeId }) => ({
activeId: activeId === id ? null : id, // toggle same id off, or set new
}));
render() {
const { activeId } = this.state;
return (
<div style={pagesStyle}>
{this.props.pages.map((page) => (
<PageButton
isActive={activeId === page.id} // <-- compute if active
onMouseOver={this.handleClick}
onClick={this.toggleActive(page.id)} // <-- enclose id in callback
key={page.id}
page={page}
/>
))}
</div>
);
}
}
Page button should consume these new props. Add the onClick handler where it makes sense and set the active classname by the isActive prop.
class PageButton extends Component {
state = {
isHovered: false,
};
handleHover = () => {
this.setState(({ isHovered }) => ({ isHovered: !isHovered }));
};
render() {
const { isHovered } = this.state;
const { isActive, onClick, page: { id, title } } = this.props;
return (
<div
id='singlePageBtnStyle'
className='page'
onMouseEnter={this.handleHover}
onMouseLeave={this.handleHover}
onClick={onClick} // <-- attach onClick handler
>
<div
className={`light ${
isHovered ? 'light-2' : [isActive ? 'light-active' : 'light']
}`}
></div>
<div
className={`sect ${
isHovered ? 'sect-2' : [isActive ? 'sect-active' : 'sect']
}`}
>
<p
dataText={title}
style={{ margin: 'auto' }}
className={`${isHovered ? 'glitch' : ''}`}
>
{title}
</p>
</div>
</div>
);
}
}

React state does not get updated

I know there's been many questions with this topic asked already, but it really feels like each and every one of them is different and I cannot find one that matches my issue closely enough.
I have a grid with draggable ItemComponents. Once selected, additional action icons show up (ItemActionIcon). I would very much like to unselect the component (and effectively hide the action icons) once one of the actions is clicked.
and so in line 77 <div onClick={() => this.setState({selected: false})} key={index}> I'm attempting to update the state of selected to false. It already works just fine in all other cases mentioned in the file. But not in this case. When I click the icon, I can see with a debugger (or with a console.log when I tried it) that the onClick action is triggered as expected and the ItemComponent even gets another call to the render method, but the this.state.selected is still set to true.
import React, {Component} from "react";
import Draggable, {DraggableBounds} from "react-draggable";
import ItemComponentAction from "./ItemComponentAction";
import ItemActionIcon from "./ItemActionIcon";
export interface Position {
x: number;
y: number;
}
export interface ItemComponentProps {
gridSquareSize: number;
canvasBounds: DraggableBounds;
margin: number;
position: Position;
}
interface ItemComponentState {
gridSquareSize: number;
canvasBounds: DraggableBounds;
margin: number;
selected: boolean;
}
export default abstract class ItemComponent extends Component<ItemComponentProps> {
protected abstract readonly icon: string;
protected abstract readonly actions: ItemComponentAction[];
state: ItemComponentState;
protected constructor(props: ItemComponentProps) {
super(props);
this.state = {
gridSquareSize: props.gridSquareSize,
canvasBounds: props.canvasBounds,
margin: props.margin,
selected: false
};
}
render() {
return (
<Draggable grid={[this.state.gridSquareSize / 2, this.state.gridSquareSize / 2]}
defaultPosition={{
x: this.state.margin + this.props.position.x * this.state.gridSquareSize,
y: this.state.margin + this.props.position.y * this.state.gridSquareSize
}}
handle=".handle"
bounds={this.state.canvasBounds}
onDrag={() => this.setState({selected: false})}
>
<div tabIndex={0}
className="handle"
onClick={() => this.setState({selected: true})}
onBlur={() => this.setState({selected: false})}
style={{
position: 'absolute',
backgroundColor: 'red',
width: this.state.gridSquareSize,
height: this.state.gridSquareSize,
cursor: "move"
}}
>
{this.icon}
{
!this.state.selected || !this.actions.length
? null
: (
<div style={{
position: 'absolute',
bottom: "0"
}}>
{
this.actions.map((action, index) => (
<div onClick={() => this.setState({selected: false})} key={index}>
<ItemActionIcon {...action}/>
</div>
))
}
</div>
)
}
</div>
</Draggable>
);
}
}
so what's the deal?
The outer <div> of your component has its own onClick handler which is setting the value of your state back to false. Try using stopPropagation() on the inner onClick handled event. That will prevent the event from propagating to the outer parent <div>, and only the inner onClick handler will execute when it is clicked on.
{
!this.state.selected || !this.actions.length ? null : (
<div
style={{
position: "absolute",
bottom: "0"
}}
>
{this.actions.map((action, index) => (
<div
onClick={e => {
e.stopPropagation();
this.setState({ selected: false });
}}
key={index}
>
<ItemActionIcon {...action} />
</div>
))}
</div>
);
}

How to make div with selected color in colorpicker

For my app, I use react color picker https://casesandberg.github.io/react-color. I must make add button, and when the user clicks on this button it needs to show div(className is saved-color-placeholder) with the selected color. So, the background of div must be like a selected color. In function saveColor, I push the color in an empty array.
This is my code
class Colorpicker extends React.Component {
constructor(props) {
super(props);
this.state = {
displayColorPicker: false,
color: {
r: '0',
g: '0',
b: '0',
a: '1',
},
colorHex: "#000",
savedColors: []
};
}
handleClick = () => {
this.setState({ displayColorPicker: !this.state.displayColorPicker })
};
handleClose = () => {
this.setState({ displayColorPicker: false })
};
handleChange = (color) => {
this.setState({ color: color })
console.log(color)
};
saveColor = (color) => {
let savedColorsArray = this.state.savedColors;
savedColorsArray.push(this.state.color)
this.setState({
savedColors: savedColorsArray
})
console.log(this.state.savedColors)
}
render() {
const styles = reactCSS({
'default': {
color: {
width: '31px',
height: '31px',
borderRadius: '20px',
background: `rgba(${this.state.color.r}, ${this.state.color.g}, ${this.state.color.b}, ${this.state.color.a})`,
},
popover: {
position: 'absolute',
zIndex: '2',
},
savedColor: {
width: '28px',
height: '28px',
borderRadius: '14px',
marginRight: '8px',
background: `rgba(${this.state.color.r}, ${this.state.color.g}, ${this.state.color.b}, ${this.state.color.a})`
}
},
});
return (
<div className="text-color-container ">
<div className="selected-color-content">
<div className="hex-placeholder">
<p>{this.state.colorHex}</p>
</div>
<div className="switch" onClick={this.handleClick}>
<div style={styles.color} />
</div>
{
this.state.displayColorPicker ?
<div className="wrapper">
<div style={styles.popover}>
<div style={styles.cover} onClick={this.handleClose} />
<SketchPicker color={this.state.color} onChange={this.handleChange} />
<div className="saved-colors">
<span >Saved colors</span>
<div className="painted-colors">
<button onClick={this.saveColor} className="btn-save-color">Add</button>
<span slassName="saved-color-placeholder">
<div style={styles.savedColor}/>
</span>
</div>
</div>
</div>
</div>
:
null
}
</div>
</div>
)
}
}
export default Colorpicker
I am not sure this is the best solution but I think it can be good enough.
You can use arrays inside your style prop. So what you could do is:
<div style={[styles.savedColor,{backgorundColor: selectedColor}]>
Hope this will helps.

Material-UI: ListItem onClick not called

I am working with React and Material-UI. I am trying to display a list and call a function when one of the items of the list is clicked.
The code below is a simplified version of my code, but it should contain all the lines concerning the issue.
The onClick function is apparently never called. What am I missing?
App.js:
import React, { Component } from 'react';
import './App.css';
import Grid from '#material-ui/core/Grid';
import Map from './Map.js';
class App extends Component {
render() {
return (
<div >
<Grid container spacing={24}>
<Grid item xs={3}>
Column text
</Grid>
<Grid item xs={9}>
<Map/>
</Grid>
</Grid>
</div>
);
}
}
export default App;
Map.js:
import React from 'react';
import List from '#material-ui/core/List';
import ListItem from '#material-ui/core/ListItem';
import ListItemText from '#material-ui/core/ListItemText';
class Map extends React.Component {
constructor(props) {
super(props);
this.state = {
sensorsList: {
isLoaded: false,
data: null,
},
};
}
componentDidMount() {
// retrieve list of sensors
fetch("https://api-of-things.plenar.io/api/sensors?size=10000")
.then(res => res.json())
.then(
(result) => {
this.setState({
sensorsList: {
isLoaded: true,
data: Object.keys(result.data).map(key => result.data[key].path),
}
});
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
this.setState({
error
});
}
)
}
selectSensor(name) {
console.log(name);
}
_renderSensorsList() {
const {sensorsList} = this.state;
return sensorsList.isLoaded && (
<div style={{
position: 'absolute',
zIndex: 1,
pointerEvents: 'none',
right: 10,
top: 10,
backgroundColor: 'white',
}}>
<List component="nav">
{ sensorsList.data.map(key =>
<ListItem button key={key} onClick={() => this.selectSensor("ciao")} >
<ListItemText primary={key} />
</ListItem>
)}
</List>
</div>
);
}
render() {
const {sensorsList, selectedSensor} = this.state;
return (
<div>
{ this._renderSensorsList() }
</div>
);
}
}
export default Map;
The problem is that in your div's style you are setting pointerEvents: 'none', preventing the click to reach the DOM event listener, remove it and it will work.
<div style={{
position: 'absolute',
zIndex: 1,
pointerEvents: 'none', //<---- remove this line
right: 10,
top: 10,
backgroundColor: 'white',
}}>
try replacing this
_renderSensorsList() {
with arrow function like this
_renderSensorsList = () => {

Resources