I have an idea that this may be because I am doing some styling things to change my radio button, but I am not sure. I am setting an onClick event that is calling my function twice. I have removed it to make sure it wasn't being triggered somewhere else and the onClick seems to be the culprit.
<div className="CheckboxContainer" onClick={() => this.changeShipping({ [k]: i }) } > <label> <div className="ShippingName"> {shipOption.carrier ? shipOption.carrier.serviceType : null}{' '} {shipOption.name} </div> <div className="ShippingPrice"> ${shipOption.amount} </div> <input type="radio" value={i} className="ShippingInput" onChange={() => this.setState({ shippingOption: { ...this.state.shippingOption, [k]: i } }) } checked={ this.state.shippingOption[k] === i ? true : false } /> <span className="Checkbox" /> </label> </div>
my function is just for now a simple console log of the shipping option:
changeShipping(shipOption){ console.log('clicked') // happening twice }
If there isn't any reason you see here why this would happen I can post the rest of the code, but there is a lot and I don't think it would pertain to this, but I think this is a good starting place.
Full code:
import React, { Component } from 'react' import fetch from 'isomorphic-fetch' import { Subscribe } from 'statable' import { FoldingCube } from 'better-react-spinkit' import styles from './styles' import { cost, cartState, userInfo, itemState, Api } from '../../state' import { removeCookies, resetCart } from '../../../injectState' export default class ShippingOptions extends Component { constructor(props) { super(props) this.state = { shippingOption: {} } this.changeShipping = this.changeShipping.bind(this) } async changeShipping(shipOption) { const shipKey = Object.keys(shipOption)[0] // if (userInfo.state.preOrderInfo.setShip[shipKey] === shipOption[shipKey]) { // return // } let updatedShipOption = {} Object.keys(shipOption).forEach(k => { updatedShipOption = userInfo.state.preOrderInfo.setShip ? { ...userInfo.state.preOrderInfo.setShip, [k]: shipOption[k] } : shipOption }) userInfo.setState({ preOrderInfo: { ...userInfo.state.preOrderInfo, setShip: updatedShipOption } }) // Make request to change shipping option const { preOrderInfo } = userInfo.state const shippingRes = await fetch(Api.state.api, { body: JSON.stringify(preOrderInfo), method: 'POST' }) .then(res => res.json()) .catch(err => { let error = '' if ( err.request && (err.request.status === 404 || err.request.status === 502) ) { error = `Error with API: ${err.response.statusText}` } else if (err.request && err.request.status === 0 && !err.response) { error = 'Something went wrong with the request, no response was given.' } else { error = err.response || JSON.stringify(err) || err } cartState.setState({ apiErrors: [error], loading: false }) }) console.log(shippingRes) } async componentDidMount() { if (cartState.state.tab === 2) { const { shipping } = userInfo.state const { items, coupon } = itemState.state let updated = { ...shipping } const names = updated.shippingFullName.split(' ') updated.shippingFirst = names[0] updated.shippingLast = names[1] delete updated.shippingFullName updated.site = cartState.state.site updated.products = items updated.couponCode = coupon updated.addressSame = userInfo.state.addressSame cartState.setState({ loading: true }) const shippingRes = await fetch(Api.state.api, { body: JSON.stringify(updated), method: 'POST' }) .then(res => res.json()) .catch(err => { let error = '' if ( err.request && (err.request.status === 404 || err.request.status === 502) ) { error = `Error with API: ${err.response.statusText}` } else if (err.request && err.request.status === 0 && !err.response) { error = 'Something went wrong with the request, no response was given.' } else { error = err.response || JSON.stringify(err) || err } cartState.setState({ apiErrors: [error], loading: false }) }) console.log(shippingRes) return shippingRes.products.forEach(product => { const regexp = new RegExp(product.id, 'gi') const updatedItem = items.find(({ id }) => regexp.test(id)) if (!updatedItem) { console.warn('Item not found and being removed from the array') const index = itemState.state.items.indexOf(updatedItem) const updated = [...itemState.state.items] updated.splice(index, 1) itemState.setState({ items: updated }) return } updatedItem.price = product.price itemState.setState({ items: itemState.state.items.map( item => (item.id === product.id ? updatedItem : item) ) }) }) updated.shippingOptions = shippingRes.shippingOptions Object.keys(updated.shippingOptions).forEach(k => { this.setState({ shippingOption: { ...this.state.shippingOption, [k]: 0 } }) updated.setShip = updated.setShip ? { ...updated.setShip, [k]: 0 } : { [k]: 0 } }) updated.success = shippingRes.success updated.cartId = shippingRes.cartId updated.locations = shippingRes.locations userInfo.setState({ preOrderInfo: updated }) cost.setState({ tax: shippingRes.tax, shipping: shippingRes.shipping, shippingOptions: Object.keys(updated.shippingOptions).length > 0 ? updated.shippingOptions : null }) cartState.setState({ loading: false, apiErrors: shippingRes.errors.length > 0 ? shippingRes.errors : null }) if (shippingRes.errors.length > 0) { removeCookies() shippingRes.errors.forEach(err => { if (err.includes('CRT-1-00013')) { itemState.setState({ coupon: '' }) } }) } } } render() { return ( <Subscribe to={[cartState, cost, itemState]}> {(cart, cost, itemState) => { if (cart.loading) { return ( <div className="Loading"> <div className="Loader"> <FoldingCube size={50} color="rgb(0, 207, 255)" /> </div> </div> ) } if (cart.apiErrors) { return ( <div className="ShippingErrors"> <div className="ErrorsTitle"> Please Contact Customer Support </div> <div className="ErrorsContact"> (contact information for customer support) </div> <div className="Msgs"> {cart.apiErrors.map((error, i) => { return ( <div key={i} className="Err"> {error} </div> ) })} </div> <style jsx>{styles}</style> </div> ) } return ( <div className="ShippingOptionsContainer"> <div className="ShippingOptions"> {cost.shippingOptions ? ( <div className="ShipOptionLine"> {Object.keys(cost.shippingOptions).map((k, i) => { const shipOptions = cost.shippingOptions[k] const updatedProducts = shipOptions.products.length === 0 ? [] : shipOptions.products.map(product => itemState.items.find( item => item.id === product.id ) ) return ( <div className="ShippingInputs" key={i}> {shipOptions.options.map((shipOption, i) => { return ( <div className="ShippingSection" key={i}> <div className="SectionTitle"> 4. {shipOption.name} Shipping Options </div> {updatedProducts.length > 0 ? ( <div className="ShippingProducts"> {updatedProducts.map((product, i) => ( <div key={i}> for{' '} {shipOption.name === 'Freight' ? 'Large' : 'Small'}{' '} {product.name} from{' '} {k.charAt(0).toUpperCase() + k.slice(1)} </div> ))} </div> ) : null} <div className="CheckboxContainer" onClick={() => this.changeShipping({ [k]: i }) } > <label> <div className="ShippingName"> {shipOption.carrier ? shipOption.carrier.serviceType : null}{' '} {shipOption.name} </div> <div className="ShippingPrice"> ${shipOption.amount} </div> <input type="radio" value={i} className="ShippingInput" onChange={() => this.setState({ shippingOption: { ...this.state.shippingOption, [k]: i } }) } checked={ this.state.shippingOption[k] === i ? true : false } /> <span className="Checkbox" /> </label> </div> </div> ) })} </div> ) })} </div> ) : null} </div> <style jsx>{styles}</style> </div> ) }} </Subscribe> ) } }
If your application is acting weird after you updated to React 18, this is simply due to the fact that the original behavior of the useEffect hook was changed to execute the effect twice instead of once.
So you've upgraded to React 18, enabled strict mode, and now all of your useEffects are getting called twice. Which would normally be fine, but you have API calls in your useEffects so you're seeing double traffic in development mode.
To handle double click events in React:Add an onClick prop to the element. Use the detail property on the event object to get the click count. If the click count is equal to 2, handle the double click event.
Its because your app
component is a wrap in StrictMode
.
<React.StrictMode> <App /> </React.StrictMode>,
If you are using
create-react-app
then it is found inindex.js
It is expected that setState
updaters will run twice in strict mode
in development. This helps ensure the code doesn't rely on them running a single time (which wouldn't be the case if an async render was aborted and later restarted). If your setState
updaters are pure functions (as they should be) then this shouldn't affect the logic of your application.
https://github.com/facebook/react/issues/12856#issuecomment-390206425
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With