programing

React에서 기능이 두 번 호출되는 이유는 무엇입니까?

topblog 2023. 3. 14. 21:22
반응형

React에서 기능이 두 번 호출되는 이유는 무엇입니까?

라디오 버튼을 바꾸기 위해 스타일링을 하고 있기 때문인 것 같습니다만, 잘 모르겠습니다.함수를 두 번 호출하는 onClick 이벤트를 설정 중입니다.다른 곳에서 트리거되지 않았는지 확인하기 위해 제거했는데 onClick이 원인인 것 같습니다.

<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>

현재 제 기능은 배송 옵션의 단순한 콘솔 로그입니다.

changeShipping(shipOption){
 console.log('clicked') // happening twice 
}

여기에 왜 이런 일이 일어나는지 이유를 알 수 없다면 나머지 코드를 게시할 수 있지만, 많은 부분이 있고 이와는 관련이 없다고 생각합니다만, 저는 이곳이 좋은 출발점이라고 생각합니다.

풀코드:

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>
    )
  }
}

그 이유는 당신의app컴포넌트는 랩인입니다.StrictMode.

<React.StrictMode>
  <App />
</React.StrictMode>,

사용하시는 경우create-react-app그리고 그것은 에서 발견됩니다.index.js

할 것으로 예상된다setState업데이트 프로그램이 2회 실행됩니다.strict mode개발중입니다.이렇게 하면 코드가 한 번 실행되는 데 의존하지 않습니다(비동기 렌더가 중단되었다가 나중에 재시작된 경우에는 해당되지 않음).만약 당신이setState업데이트 프로그램은 순수 함수이므로 응용 프로그램의 로직에는 영향을 주지 않습니다.

https://github.com/facebook/react/issues/12856#issuecomment-390206425

이 문제는 리액트와 관련된 것이 아니라 html과 관련된 것입니다.기본적으로 라벨을 클릭하면 라벨과 관련된 입력 요소의 onClick 이벤트도 트리거됩니다.이 경우 onClick 이벤트는 라벨과 입력 모두에 첨부됩니다.따라서 라벨을 클릭하면 이벤트가 두 번(라벨과 관련된 입력에 대해 한 번) 실행됩니다.

편집: onClick 청취자를 입력에 연결하면 문제를 해결할 수 있습니다.

를 사용하여 두 번 호출을 방지하다e.preventDefault().

changeShipping(e){
   e.preventDefault();
   console.log('clicked');
}

e.stopPropagation()탐구할 가치가 있습니다.onMouseDown 이벤트를 처리하고 있었는데 prevent Default로 인해 여러 콜이 차단되지 않았습니다.

https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation

이 경우 중복 콜을 피하기 위해 둘 다 필요합니다.

<React.StrictMode>
  <App />
</React.StrictMode>

니샤르그 샤 출신

e.preventDefault();

암루스 출신

또한 기능 컴포넌트에 대해 useEffect() 메서드가 2회 호출되는 경우 bcoz입니다.FC(기능 컴포넌트) 및 의존성 목록에서 바인드하기 위해 사용되는 모든 메서드는 useEffect가 1개뿐입니다.변경 후 다음과 같습니다.

useEffect(() => {
    console.log("AFTER CHANGE : ", data) // move to below method
    handleSubmit.bind(this);
    handleCancel.bind(this);
    testChange.bind(this);
}, [
    data // move to below method
]);

useEffect(() => {
    console.log("AFTER CHANGE : ", data)
}, [data]);
  1. , 것은 의존자 리스트가 없습니다.bindings
  2. onChange()의 dependencyList가

이게 도움이 됐으면 좋겠다.해피 코딩...

언급URL : https://stackoverflow.com/questions/50819162/why-is-my-function-being-called-twice-in-react

반응형