简体   繁体   中英

ReactJS- useEffect running again and again

I am working on the ReactJS Project and in there I want a component which does something every time it renders but not every time something change in it.

Dashboard.js

import React, { useCallback, useEffect, useState } from 'react'
import Product from './Product'
import { database } from '../../firebase/firebase'

const Dashboard = () => {

    const [defaults, setDefaults] = useState([])
    const [firstKey, setFirstKey] = useState('')
    const [lastKey, setLastKey] = useState('')

    const convertToArray = (data) => {
        const tempArray = []
        for (let product in data) {
            tempArray.push({
                id: product,
                amount: data[product]['amount'],
                productName: data[product]['productName']
            })
        }
        return tempArray
    }

    const nextButtonListener = async () => {
        const nextData = await (await database.ref('/system').orderByKey().startAfter(lastKey).limitToFirst(10).once('value')).val()

        const keys = Object.keys(nextData)

        setFirstKey(keys[0])
        setLastKey(keys[keys.length - 1])
        setDefaults(oldDefaults => convertToArray(nextData))
    }

    const previousButtonListener = async () => {
        const previousData = await (await database.ref('/system').orderByKey().endBefore(firstKey).limitToLast(10).once('value')).val()

        const keys = Object.keys(previousData)

        setFirstKey(keys[0])
        setLastKey(keys[keys.length - 1])
        setDefaults(oldDefaults => convertToArray(previousData))
    }

    const firstPageLoad = async () => {
        const firstPage = await (await database.ref('/system').orderByKey().limitToFirst(10).once('value')).val()
        const keys = Object.keys(firstPage)
        setFirstKey(keys[0])
        setLastKey(keys[keys.length - 1])
        setDefaults(oldDefaults => convertToArray(firstPage))
    }

    useEffect(() => {
        console.log('Called')
        firstPageLoad()
        document.querySelector('#nextButton').addEventListener('click', nextButtonListener)
        document.querySelector('#previousButton').addEventListener('click', previousButtonListener)

        return () => {
            document.querySelector('#nextButton').removeEventListener('click', nextButtonListener)
            document.querySelector('#previousButton').removeEventListener('click', previousButtonListener)
        }
    }, [])

    return (
        <div>
            <h2>Dashboard</h2>
            {defaults.map(product => {
                return (<Product key={product.id} product={{ id: product.id, amount: product.amount, name: product.productName }} />)
            }
            )}
            <button id="nextButton">Next</button>
            <button id="previousButton">Previous</button>
        </div>
    )
}

export default Dashboard

Product.js

import React, { useContext, useEffect } from 'react'
import { cartContext } from '../context/appContext'
import { addProduct, removeProduct } from '../actions/cart'

const Product = ({ product, isCart }) => {

    const { cartDispatch } = useContext(cartContext)

    return (
        <div>
            <h3>{product.name}</h3>
            <p>Amount: {product.amount}</p>
            {
                isCart ?
                    (<button onClick={() => { cartDispatch(removeProduct(product.id)) }}>Remove</button>) : (<button onClick={() => { cartDispatch(addProduct(product)) }}>Add to Cart</button>)
            }
        </div>
    )
}

export default Product

Now, everything renders perfectly but whenever I click on the button in Product , useEffect in Dashboard runs even though I have provided empty array as second arguement in useEffect method in MyComponent

First, you can't use async function in an effect, but you can call an async function instead. Secondly, somewhere you have some conditions that hide and show this component, otherwise, this effect should run on mount only.

You have not to use async with useEffect . Otherwise, I think you are going to get response from Firebase, you can use useCallback with async and await rather than useEffect .

import React, { useCallback, useEffect, useState } from 'react';

const MyComponent = () => {
  const [someState, setSomeState] = useState([]);

  const getData = useCallback(async () => {
    try {
      const response = await fetch('/api/v1/get-data-firebase', { responseType: 'json' });
      setSomeState(response.data);
      // or axios.get()
    } catch (err) {
      console.error(err);
    }
  }, []);

  useEffect(() => {
    getData();
  }, [getData]);

  return (
    <div>
      {
        someState.map(item => (
          <SomeComponent key={item.id} />
        )
      }
   </div>
 )
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM