Ternary operator not working as desired on state change - javascript

I'm trying to build a map that will show whisky distilleries as icons on the map. When one of the distillery markers is clicked, the state is updated to hold an object with that distillery data so a popup can render with relevant info. I've got a hover effect that I want to persist after clicking for as long as the state holds the obj.
I have the following ternary operator that should add 'clicked' when selectedDistillery is truthy, but the class is not applied.
className={`marker-btn ${selectedDistillery ? 'clicked' : ''}`}
The popup renders fine on click so not really sure what the issue is.
Here's the entire component
import 'mapbox-gl/dist/mapbox-gl.css';
import './App.css';
import React, { useState, useEffect } from 'react'
import ReactMapGl, { Marker, Popup } from "react-map-gl";
import * as distilleries from "./data/dist-locations.json";
const App = () => {
const [viewport, setViewport] = useState({
latitude: 56.770720743612365,
longitude: -4.2724397531559655,
width: "90vw",
height: "90vh",
zoom: 6,
});
const [selectedDistillery, setSelectedDistillery] = useState(null);
useEffect(() => {
const listener = (e) => {
if(e.key === 'Escape'){
setSelectedDistillery(null);
}
}
window.addEventListener('keydown', listener);
return () => {
window.removeEventListener('keydown', listener);
}
}, [])
console.log(selectedDistillery);
return (
<div className="main-container">
<div className="map-container">
<ReactMapGl
{...viewport}
mapboxApiAccessToken={process.env.REACT_APP_API_KEY}
onViewportChange={(viewport => { setViewport(viewport) })}
mapStyle="mapbox://styles/vdiad/ckkq0g4201s4r17peswejsg82"
>
{distilleries.features.map(distillery => {
return(
<Marker key={distillery.id} latitude={distillery.geometry.coordinates[1]} longitude={distillery.geometry.coordinates[0]} >
<button
className={`marker-btn ${selectedDistillery ? 'clicked' : ''}`}
onClick={()=>{
setSelectedDistillery(distillery);
}}
>
<img src="/barrel1.svg" alt="whisky barrel img" />
</button>
</ Marker>
)
})}
{selectedDistillery && (
<div className="popup">
<Popup
latitude={selectedDistillery.geometry.coordinates[1]}
longitude={selectedDistillery.geometry.coordinates[0]}
onClose={()=>{setSelectedDistillery(null)}}
>
<h3>{selectedDistillery.properties.NAME}</h3>
<p>Founded in: </p>
<p>Region: </p>
</ Popup>
</div>
)}
</ReactMapGl>
</div>
</div>
)
}
export default App

Related

ReactJs Hiding Some HTML depend on situation

I want to make the price tag also some other HTML contents hide/show depending on some data entry.
for example, if I get True it should be visible prices if it's gonna be False it must hide.
I'm sharing some code of my pages please give me ideas.
Thank you.
// react
import React from 'react';
// third-party
import classNames from 'classnames';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
// application
import AsyncAction from './AsyncAction';
import Points from './Points';
import { cartAddItem } from '../../store/cart';
import { Quickview16Svg } from '../../svg';
import { quickviewOpen } from '../../store/quickview';
import { url } from '../../services/utils';
function ProductCard(props) {
const {
product,
layout,
quickviewOpen,
cartAddItem,
} = props;
const containerClasses = classNames('product-card', {
'product-card--layout--grid product-card--size--sm': layout === 'grid-sm',
'product-card--layout--grid product-card--size--nl': layout === 'grid-nl',
'product-card--layout--grid product-card--size--lg': layout === 'grid-lg',
'product-card--layout--list': layout === 'list',
'product-card--layout--horizontal': layout === 'horizontal',
});
let badges = [];
let image;
let price;
let features;
if (product.badges.includes('sale')) {
badges.push(<div key="sale" className="product-card__badge product-card__badge--sale">Sale</div>);
}
if (product.badges.includes('hot')) {
badges.push(<div key="hot" className="product-card__badge product-card__badge--hot">Hot</div>);
}
if (product.badges.includes('new')) {
badges.push(<div key="new" className="product-card__badge product-card__badge--new">New</div>);
}
badges = badges.length ? <div className="product-card__badges-list">{badges}</div> : null;
if (product.images && product.images.length > 0) {
image = (
<div className="product-card__image product-image">
<Link to={url.product(product)} className="product-image__body">
<img className="product-image__img" src={product.images[0]} alt="" />
</Link>
</div>
);
}
if (product.discountPrice) {
price = (
<div className="product-card__prices">
<span className="product-card__new-price"><Points value={product.price} /></span>
{' '}
<span className="product-card__old-price"><Points value={product.discountPrice} /></span>
</div>
);
} else {
price = (
<div className="product-card__prices">
<Points value={product.price} />
</div>
);
}
if (product.attributes && product.attributes.length) {
features = (
<ul className="product-card__features-list">
{product.attributes.filter((x) => x.featured).map((attribute, index) => (
<li key={index}>{`${attribute.name}: ${attribute.values.map((x) => x.name).join(', ')}`}</li>
))}
</ul>
);
}
return (
<div className={containerClasses}>
<AsyncAction
action={() => quickviewOpen(product.slug)}
render={({ run, loading }) => (
<button
type="button"
onClick={run}
className={classNames('product-card__quickview', {
'product-card__quickview--preload': loading,
})}
>
<Quickview16Svg />
</button>
)}
/>
{badges}
{image}
<div className="product-card__info">
<div className="product-card__name">
<Link to={url.product(product)}>{product.name}</Link>
<br />
<br />
</div>
{features}
</div>
<div className="product-card__actions">
<div className="product-card__availability">
Availability:
<span className="text-success">In Stock</span>
</div>
{price}
<div className="product-card__buttons">
<AsyncAction
action={() => cartAddItem(product)}
render={({ run, loading }) => (
<React.Fragment>
<button
type="button"
onClick={run}
className={classNames('btn btn-primary product-card__addtocart', {
'btn-loading': loading,
})}
>
Add To Cart
</button>
<button
type="button"
onClick={run}
className={classNames('btn btn-secondary product-card__addtocart product-card__addtocart--list', {
'btn-loading': loading,
})}
>
Add To Cart
</button>
</React.Fragment>
)}
/>
</div>
</div>
</div>
);
}
ProductCard.propTypes = {
/**
* product object
*/
product: PropTypes.object.isRequired,
/**
* product card layout
* one of ['grid-sm', 'grid-nl', 'grid-lg', 'list', 'horizontal']
*/
layout: PropTypes.oneOf(['grid-sm', 'grid-nl', 'grid-lg', 'list', 'horizontal']),
};
const mapStateToProps = () => ({});
const mapDispatchToProps = {
cartAddItem,
quickviewOpen,
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(ProductCard);
Here I want to hide prices in some onload situations. This is my homepage Carousel.
You can use code like this. It will only render the component if the boolean evaluates to a truthy value.
const { isVisible } = this.props; // Or wherever you want to get your boolean from
return (
<div>
{isVisible && <MyComponent />}
</div>
You refer to Conditional rendering, there are a couple ways to do that:
<div>
{someCondition && <p>The condition is true</p>}
</div>
Or if you want a if else rendering:
<div>
{someCondition ? <p>The condition is true</p> : <p>The condition is false</p>}
</div>
You can find more info in react docs

React modal custom component not showing the correct data

I have built this modal component using react hooks. However the data that the modal shows when it pops up its incorrect (it always shows the name property for last element in the array).
//Modal.js
import ReactDOM from 'react-dom';
const Modal = ({ isShowing, hide, home_team }) => {return isShowing ? ReactDOM.createPortal(
<React.Fragment>
<div className="modal-overlay"/>
<div className="modal-wrapper">
<div className="modal">
<div className="modal-header">
<a>Home team: {home_team}</a>
<button type="button" className="modal-close-button" onClick={hide}>
</button>
</div>
</div>
</div>
</React.Fragment>, document.body
) : null;}
export default Modal;
// Main component
const League = ({ league, matches }) =>{
const {isShowing, toggle} = useModal();
return (
<Fragment>
<h2>{league}</h2>
{
matches.map((
{
match_id,
country_id,
home_team
}
) =>
{
return (
<div>
<p>{match_id}</p>
<button className="button-default" onClick={toggle}>Show Modal</button>
<a>{home_team}</a>
<Modal
isShowing={isShowing}
hide={toggle}
home_team={home_team}
/>
<p>{home_team}</p>
</div>
)
})
}
</Fragment>
)};
This is what matches data set looks like:
[{
match_id: "269568",
country_id:"22",
home_team: "Real Kings"
},
{
match_id: "269569",
country_id:"22",
home_team: "Steenberg United"
},
{
match_id: "269570",
country_id:"22",
home_team: "JDR Stars "
},
{
match_id: "269571",
country_id:"22",
home_team: "Pretoria U"
},
]
I am not sure whats going on because the data seems to be passed fine.
<p>{home_team}</p>
in the main component is showing everytime the expected property, however the Modal always shows the last home_team item in the array (e.g.Pretoria U)
you need to call useModal inside of the map function. otherwise you will open on toggle all Modals and the last one overlaps the others
const HomeTeam = ({ match_id, country_id, home_team }) => {
const {isShowing, toggle} = useModal();
return (
<div>
<p>{match_id}</p>
<button className="button-default" onClick={toggle}>Show Modal</button>
<a>{home_team}</a>
<Modal
isShowing={isShowing}
hide={toggle}
home_team={home_team}
/>
<p>{home_team}</p>
</div>
)
}
const League = ({ league, matches }) => (
<Fragment>
<h2>{league}</h2>
{ matches.map((match) => <Hometeam {...match} /> }
</Fragment>
);

Reload table after deleting an item from it with React

Using React.Js, I created a delete function to delete an item from a table. the delete is working fine but what I want to do is that after deleting I want the tables to be dynamically updated to show only the items left. Now after the delete I have to refresh manually the page or go to another page and comeback to see the items left after the delete
This is the code built so far :
import React, { Component } from "react";
import { Card, Button, Select,/* message, */ Form, Tooltip } from "antd";
import extraitMP3 from "./data";
import { arrayMove, SortableHandle } from "react-sortable-hoc";
import ContainerHeader from "components/ContainerHeader/index";
import { getExtraitMp3, hideMessageUpdate, showUpdateLoader, updateMp3Visibilite } from "appRedux/actions/Comedien";
import { deleteMp3Request } from "../../../appRedux/services/extraitMp3Service"
import { connect } from "react-redux";
import { NotificationContainer, NotificationManager } from "react-notifications";
import { userSignOut } from "appRedux/actions/Auth";
import { displayIcon } from '../../../util/Icon.js';
import CircularProgress from "components/CircularProgress";
import { Modal } from "antd";
const extraitMP32 = [extraitMP3];
const confirm = Modal.confirm;
const Option = Select.Option;
const DragHandle = SortableHandle(() =>
<span className="gx-draggable-icon gx-pt-2">
<i className="icon icon-menu" style={{ fontSize: 25 }} />
</span>);
class ListExtrait extends Component {
onSortEnd = ({ oldIndex, newIndex }) => {
this.setState({
extraitMP3: arrayMove(this.state.extraitMP3, oldIndex, newIndex),
});
};
constructor() {
super();
this.state = {
extraitMP3: extraitMP32[0],
nombreMP3: {
rechercheExtraits: 0,
recherchePossible: 0,
extraitFiche: 0,
extraitFichePossible: '',
extraitArchives: 0,
extraitArchivesPossible: '',
},
loader: false,
}
}
componentDidMount() {
this.props.getExtraitMp3();
}
componentDidUpdate() {
}
static getDerivedStateFromProps(nextProps, prevState,/* nextProps2,prevState2 */) {
if (nextProps.extraitMP3 !== prevState.extraitMP3 && nextProps.extraitMP3) {
return { extraitMP3: nextProps.extraitMP3 };
}
else return null;
}
showDeleteConfirmation(value, id, index, thisHandler) {
confirm({
title: 'Voulez vous supprimer cette audio ?',
content: '',
okText: 'Oui, je confirme',
okType: 'danger',
cancelText: 'Non',
onOk() {
deleteMp3Request(id);
const { extraitMP3 } = thisHandler.state;
Object.keys(extraitMP3).splice(index, 1);
NotificationManager.success("le fichier audio est supprimé avec succès !", "");
},
onCancel() {
},
});
}
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.showUpdateLoader();
this.props.updateMp3Visibilite(values);
}
});
};
render() {
const { loader, extraitMP3 } = this.state;
const selectOptions = new Map([
[1, "Visible dans la recherche et sur ma fiche"],
[2, "Visible sur ma fiche uniquement"],
[3, "Masqué"],
]);
console.log('extraitMP3', extraitMP3)
function handleChangeSelect(value) {
console.log(`selected ${value}`);
}
return (
<div>
{loader ? <CircularProgress className="gx-loader-400" /> : Object.keys(extraitMP3).map((ids, index) => {
return (
<Card>
<li key={ids}>
<Card styleName="gx-card-list icon icon-data-display gx-mr-2 gx-text-blue gx-fs-xl">
<div className="gx-media-body">
{extraitMP3[ids].Typenom}
{extraitMP3[ids].TypeIcon != null &&
displayIcon(extraitMP3[ids].TypeIcon)
}
</div>
{Object.keys(extraitMP3[ids].TypeMp3List).map(idJson => {
return (
<div className="gx-main-content gx-mb-4">
<ContainerHeader match={this.props.match} />
<div className="gx-contact-item gx-dragndrop-item">
<DragHandle />
<div className="gx-col gx-job-title ">
{extraitMP3[ids].TypeMp3List[idJson].intitule}
</div>
{extraitMP3[ids].TypeMp3List[idJson].interpretation1Icon !== '' &&
<Tooltip title={extraitMP3[ids].TypeMp3List[idJson].interpretation1Nom}>
{displayIcon(extraitMP3[ids].TypeMp3List[idJson].interpretation1Icon)}
</Tooltip>
}
{extraitMP3[ids].TypeMp3List[idJson].interpretation2Icon !== '' &&
<Tooltip title={extraitMP3[ids].TypeMp3List[idJson].interpretation2Nom}>
{displayIcon(extraitMP3[ids].TypeMp3List[idJson].interpretation2Icon)}
</Tooltip>
}
{extraitMP3[ids].TypeMp3List[idJson].interpretation3Icon !== '' &&
<Tooltip title={extraitMP3[ids].TypeMp3List[idJson].interpretation3Nom}>
{displayIcon(extraitMP3[ids].TypeMp3List[idJson].interpretation3Icon)}
</Tooltip>
}
{extraitMP3[ids].TypeMp3List[idJson].langueIcon !== '' &&
<div className="gx-col gx-job-title gx-d-sm-flex gx-text-truncate gx-px-8">
<Tooltip title={extraitMP3[ids].TypeMp3List[idJson].langueNom}>
<i className={`flag flag-24 gx-mr-2 ${extraitMP3[ids].TypeMp3List[idJson].langueIcon}`} />
</Tooltip>
</div>
}
<div className="gx-col gx-job-title gx-d-sm-flex gx-text-truncate gx-px-8">
<Select
showSearch
style={{ width: '100%' }}
placeholder="Selection la choix de votre numéro de téléphone "
optionFilterProp="children"
onChange={handleChangeSelect}
defaultValue={selectOptions.get(extraitMP3[ids].TypeMp3List[idJson].visibilite)}
filterOption={(input, Option) => Option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
>
{[...selectOptions].map(([value, label]) => <Option value={value}> {label} </Option>)}
</Select>
</div>
<div className="gx-col gx-job-title gx-d-sm-flex gx-text-truncate gx-px-8">
<i className="icon icon-edit gx-fs-xl gx-text-gris" />
</div>
<div className="gx-col gx-job-title gx-d-sm-flex gx-text-truncate gx-px-8">
<span className="gx-pointer">
<i className="icon icon-trash gx-pointer gx-text-danger gx-fs-xxl"
id={extraitMP3[ids].TypeMp3List[idJson].id}
onClick={e => this.showDeleteConfirmation(e.target.value, extraitMP3[ids].TypeMp3List[idJson].id, index, this)} />
</span>
</div>
</div>
</div>
)
})}
<NotificationContainer />
<Button type="primary" htmlType="submit" labelCol={{ xs: 24, sm: 5 }} wrapperCol={{ xs: 24, sm: 12 }}>
Enregistrer
</Button>
</Card>
</li>
</Card>
)
})}</div>
)
}
}
const VisibiliteFormMp3 = Form.create()(ListExtrait);
const mapStateToProps = ({ comedien }) => {
const {
extraitMP3,
alertMessageUpdate,
showMessageUpdate
} = comedien;
return {
extraitMP3,
alertMessageUpdate,
showMessageUpdate
}
};
export default connect(
mapStateToProps,
{
userSignOut,
getExtraitMp3,
hideMessageUpdate,
showUpdateLoader,
updateMp3Visibilite
})(VisibiliteFormMp3);
extraitMP3 is an object of objects that's why I used Object.keys(extraitMP3)
I didn't know how to update the state correctly.
this is the view :
You should put your data in state and then change the state. After changing the state the page automatically re-rendered and changed data of your state will be shown.
So in your delete function simply delete your chosen data and give your remaining data to your state.
Hope this helps, feel free to ask questions if I couldn't explain myself clearly.
I believe you can do this by calling something like and then just call this from within your delete
refreshMp3(){
this.setState({getExtraitMp3: !this.state.getExtraitMp3});}
One of the ideas of React is to make the functionality you ask for simple to implement and it would update automatically. I'm going to abstract a bit from your example. Think about your data in terms of what updates along with the UI. This way we can simplify your component. You have some items that you put in a table. Each item is a row and can be inside an array. We put that array in the state.
class ListExtrait extends Component {
constructor() {
super();
this.state = {
rowsForTable: [...],
somethingElse...
}
...
Then in the JSX in the render method you can render the table rows using map:
rowsForTable.map(item => <div/li/whatever>{item.name or something else}</div>
This way whenever an item is gone from rowsForTable the component will automatically update it's view and the table rows will be up to date.
You can simply call the function while clicking the delete button say deleteHandler. On that function call the api you have made to delete the item and then after successfull delete again call the api that will show the item from database after certain time interval.
Code:
import React, { Component } from 'react'
export default class show_schedule extends Component {
render() {
state={
}
show_item_after_delete=()=>{
setTimeout(()=>{
axios.get(`http://127.0.0.1:8000/account/api/show_item/`).then(res=>{
console.log('delete schedule data ',res.data)
})
},500)
}
deleteHandler=()=>{
axios.delete(`http://127.0.0.1:8000/account/api/delete_item/${id}`).then(res=>{
console.log(res.data)
})
this.show_item_after_delete()
}
return (
<div>
<button onClick={this.deleteHandler}>Delete</button>
</div>
)
}
}

react.js antd modal wrong behaviour

So I have this big messy component, I will try to slim it down, however keep most of it since I am unsure at this point what could be cause.
The issue is, that the game works as expected. When it is time for the modal to render, it appears at the bottom left of the page, with no styling floating left. The functionality however works as expected, the buttons work and it displays the raw content.
import { Modal } from 'antd';
//rest of imports
const initialState = {
visible: false,
streak: 0,
score: 0,
turn: 0,
previousPicks: [],
result: { result: "", player: "", computer: "" }
};
class Game extends React.Component {
constructor(props) {
super(props);
this.turnLimit = 10;
this.state = initialState;
}
componentWillUnmount() {
this.setState(initialState)
}
updateScore = () => {
//handles score
}
updatePreviousPicks = () => {
//update game data
}
onClickHandler = async (choice) => {
//fetching data from backend
self.showModal();
}
getAIResult = () => {
//
}
showModal = () => {
if (this.state.turn === 10) {
this.setState({
visible: true,
});
}
}
handleOk = () => {
this.setState(initialState)
}
handleCancel = () => {
this.setState(initialState)
}
render() {
return (
<div>
<div>
<Modal
title="Basic Modal"
centered={true}
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}></Modal>
</div>
<div className="container">
<div id="rockDiv" className={`choice`} onClick={() => this.onClickHandler("rock")}>
<Choices choice="rock"></Choices>
</div>
<div id="paperDiv" className={`choice`} onClick={() => this.onClickHandler("paper")}>
<Choices choice="paper"></Choices>
</div>
<div id="scissorsDiv" className={`choice`} onClick={() => this.onClickHandler("scissors")}>
<Choices choice="scissors"></Choices>
</div>
<Score score={this.state.score} bonus={this.state.streak} turn={this.state.turn} />
<div id="PlayerResult" className={this.state.result.result} >
{this.state.turn >= 1 ? <p>You</p> : <p></p>}
<Answer choice={`${this.state.result.player}`} />
</div>
<div id="AIResult" className={this.getAIResult()} >
{this.state.turn >= 1 ? <p>AI</p> : <p></p>}
<Answer choice={`${this.state.result.computer}`} />
</div>
</div>
</div>
)
}
}
export default Game
I have tried removing all CSS from the component, and still the modal does not show with the default antd design?
As I understand that current style you have doesn't like example of Antd.
Missing is you didn't import styles of Antd like this.
import { Modal, Button } from "antd";
import "antd/dist/antd.css";
Just need import style you will have what you need.
You can check my example here https://codesandbox.io/embed/8lr93mw8yj
<Modal
title="Basic Modal"
centered="true"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}></Modal>
You do not need to wrap the "true" in brackets here as you are not calling a variable.

check function is called in child component

I am trying to make a custom dropdown but with custom children component. Within the children custom component, there's an onChange event.
The problem now is whenever I trigger the onChange which is for the checkbox, the dropdown is closed.
https://codesandbox.io/s/lr677jv7l7
Partial code
render() {
const { className, onOpen, children } = this.props
const { openItems, selectedItem } = this.state
return (
<div className={classnames('customDropdown', className)}>
<div tabIndex="1"
onBlur={() => { this.setState({ openItems: false }) }}
onFocus={() => { this.setState({ openItems: true }); onOpen && onOpen() }}>
<button className="btn">
{selectedItem}
</button>
<div className={classnames('items', { 'show': openItems === true, 'hide': openItems === false })}>
{children && children}
</div>
</div>
</div>
)
}
You need to get rid of following line:
onBlur={() => { this.setState({ openItems: false }) }}
It basically says that when your div wrapping the button loses focus (eg when you click the checkbox) it should set the state.openItems variable to false and therefore it closes the dropdown.
Edit:
Check out working example here: https://codesandbox.io/s/jnq2rqwr53.
Basically use onClick instead of blur and then you add click event to your document, so anytime user clicks anywhere on the document it calls your hide method and closes the modal. This way the selected checkbox gets checked, but if you want to dropdown to stay open after the selection you'll need to somehow tell the hide function not to execute if user clicked on the checkbox. I did it using ids and simple condition guard at the beginning of the hide method.
Code looks like this:
Hello.js
import React, { Component } from 'react';
import classnames from 'classnames'
export default class CustomDropdown extends Component {
constructor() {
super()
this.state = {
openItems: false,
selectedItem: 'Please select'
}
this.show = this.show.bind(this);
this.hide = this.hide.bind(this);
}
show() {
this.setState({openItems: true});
document.addEventListener("click", this.hide);
}
hide(e) {
if (e.target.id === "1" || e.target.id === "2") {
return false;
}
this.setState({openItems: false});
document.removeEventListener("click", this.hide);
}
render() {
const { className, onOpen, children } = this.props
const { openItems, selectedItem } = this.state
return (
<div className={classnames('customDropdown', className)}>
<div tabIndex="1">
<button className="btn" onClick={this.show}>
{selectedItem}
</button>
<div className={classnames('items', { 'show': openItems === true, 'hide': openItems === false })}>
{children && children}
</div>
</div>
</div>
)
}
}
index.js
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './styles.css';
const styles = {
fontFamily: 'sans-serif',
textAlign: 'center'
};
class App extends Component {
constructor() {
super()
}
changeCheckbox = () => {
console.log('something')
}
render(){
return(
<div style={ styles }>
<Hello>
<div>
my checkbox 1
<input type="checkbox" onChange={this.changeCheckbox} id="1" />
</div>
<div>
my checkbox 2
<input type="checkbox" onChange={this.changeCheckbox} id="2" />
</div>
</Hello>
</div>
)
}
}
render(<App />, document.getElementById('root'));

Categories

Resources