0

I am new to using React Hooks with stateless components. I usually use stateful components when I have to use state. My present app requires the context API which could only be used in functional Components. I have rewritten most of the code I need to work but one brings an error "Error: Too many re-renders. React limits the number of renders to prevent an infinite loop."

The Component I am trying to convert to a functional one

class pizzas extends Component {
  state ={
    pizzas: [
      {id:1, name: 'Chicken Curry', ingredients: 'Red onions, bell peppers, chicken, pineapple, mozzarella, tomato sauce, curry, chili peppers', price: '3100', image: chickenCurry },
      {id:2, name: 'Pepperoni Fresh', ingredients: 'Pepperoni, mozzarella, green peppers, pizza sauce', price: '2700', image: pepperoniFresh },
      {id:3, name: 'Chicken BBQ', ingredients: 'Chicken, red onions, corn, mozzarella, bbq sauce, tomato sauce', price: '2700', image: chickenBbq },
      {id:4, name: 'Shawarma Pizza', ingredients: 'Mayonnaise & ketchup, spicy chicken, red onions, tomatoes, mozzarella', price: '3100', image: sharwarmaPizza },
      {id:5, name: 'Chicken Suya', ingredients: 'Mayonnaise, spicy sauce, spicy chicken, bell peppers, red onions, suya sauce, tomato sauce, mozzarella, suya spice', price: '2700', image: chickenSuya },
      {id:6, name: 'Pepperoni', ingredients: 'Pepperoni, mozzarella, tomato sauce', price: '2700', image: pepperoni },
      {id:7, name: 'Beef Suya', ingredients: 'Mayonnaise, spicy sauce, spicy meatballs, bell peppers, red onions, mozzarella, suya sauce, tomato sauce, suya spice', price: '2700', image: beefSuya },
      {id:8, name: 'Chicken Supreme', ingredients: 'Spicy sauce, chicken and spicy chicken, mushrooms, bell peppers, olives, red onions, mozzarella, tomato sauce', price: '3100', image: chickenSupreme },
      {id:9, name: 'Sweet Chili Chicken', ingredients: 'Spicy sauce, chicken, chili pepper, mozzarella, sweet chili sauce, tomato sauce', price: '2700', image: chickenCurry },
      {id:10, name: 'Spicy Mixed Pizza', ingredients: 'Spicy sauce, spicy meatballs, spicy chicken, chili pepper, corn, mozzarella, buffalo sauce, tomato sauce', price: '3100', image: spicyMixedPizza },
      {id:11, name: 'Margherita', ingredients: 'Mozarella, tomato sauce', price: '2200', image: margherita },
      {id:12, name: 'Super Meaty', ingredients: 'Chicken, pepperonni, sausages, mozzarella, tomato sauce', price: '3100', image: superMeaty },
      {id:13, name: 'Cheesy Chicken', ingredients: 'Chicken, tomatoes, cheddar, mozzarella, cheese sauce', price: '2700', image: cheesyChicken },
      {id:14, name: 'Cheeseburger Pizza', ingredients: 'Beef, tomatoes, red onions, cheddar, mozzarella, mayonnaise & ketchup, tomato sauce', price: '3100', image: cheeseBurger },
      {id:15, name: 'Meaty Overload', ingredients: 'Spicy sauce, pepperonni, spicy meatballs, chicken, sausages, mozzarella, tomato sauce', price: '3400', image: meatyOverload },
      {id:16, name: 'Meaty BBQ', ingredients: 'Beef, pepperonni, sausages, mozzarella, bbq sauce, tomato sauce', price: '3100', image: meatyBbq },
      {id:17, name: 'Hawaiian', ingredients: 'Chicken, pineapple, mozzarella, sweet chili sauce, tomato sauce', price: '2700', image: hawaiian },
      {id:18, name: 'Veggie Overload', ingredients: 'Mushrooms, bell peppers, corn, olives, red onions, tomatoes, mozzarella, tomato sauce', price: '3100', image: veggieOverload }
    ],
    showModal: false,
    selectedPizza: null,
    orders: []
    
  }
  toggleModalHandler = (p)=>{
    this.setState({showModal: !this.state.showModal, selectedPizza: p});
    
  }
  addToOrders = (productName, productIngredients, productPrice, productImage, p)=>{
    this.setState(prevState=>({
      orders: [...prevState.orders, productImage, productName, productIngredients, productPrice]
    }))
  }
  render(){
  const pizza = this.state.pizzas;
  
  return (
   <Aux>
     { this.state.showModal?
       <Backdrop clicked={this.toggleModalHandler}/>: null}
   { this.state.showModal ?
    
     <Modal 
     clicked={this.toggleModalHandler}
     sendRequest={this.sendingRequestHandler}
     ingredients={this.state.selectedPizza.ingredients} 
     price={this.state.selectedPizza.price} 
     image={this.state.selectedPizza.image} 
     name={this.state.selectedPizza.name} 
     key={this.state.pizzas.id}       
     addToOrders={this.addToOrders}/>: null}
     <div className={styles.Pizza} id="pizza">
             <h1>Pizza</h1>
      <div className={styles.PizzaContainer}>
         {pizza.map(p=>{
           
    return <div>
      <div className={styles.PizzaCard}>
        <div className={styles.PizzaCardHeader}>
           <img src={p.image} alt="pizza"/>
           <h1>{p.name}</h1>
          <p>{p.ingredients}</p>
        </div>
        <div className={styles.PizzaCardFooter}>
           <h3>from ₦{p.price}</h3>
           <button onClick={()=>this.toggleModalHandler(p)}>Select</button>
        </div>  
    </div>
    </div>
  })}
      </div>
      </div>
 
  </Aux>
  )

}
}
export default pizzas;

What I have done so far

const Pizzas = () => {

  const [showModal, setModal] = useState(false);
  const [selectedPizza, setSelectedPizza] = useState(null)
  const { pizzaproducts } = useContext(ProductsContext)
  const toggleModalHandler = (p) => {
    setModal(true);
    setSelectedPizza(p)
  }
  return (
    <Aux>
      {setModal(true) ?
        <Backdrop clicked={toggleModalHandler} /> : null}
      {setModal(true) ?
        <Modal
          clicked={toggleModalHandler}
          ingredients={selectedPizza.ingredients}
          price={selectedPizza.price}
          image={selectedPizza.image}
          name={selectedPizza.name}
        /> : null}
      <div className={styles.Pizza} id="pizza">
        <h1>Pizza</h1>
        <div className={styles.PizzaContainer}>
          {pizzaproducts.map(p => {

            return <div>
              <div className={styles.PizzaCard}>
                <div className={styles.PizzaCardHeader}>
                  <img src={p.image} alt="pizza" />
                  <h1>{p.name}</h1>
                  <p>{p.ingredients}</p>
                </div>
                <div className={styles.PizzaCardFooter}>
                  <h3>from ₦{p.price}</h3>
                  <button onClick={() => toggleModalHandler(p)}>Select</button>
                </div>
              </div>
            </div>
          })}
        </div>
      </div>
    </Aux>
  )
}
export default Pizzas;

How best can I convert this? I am guessing this error is from the togglemodalhandler. I am new to hooks.

6
  • I think what you're trying to accomplish is to check to see if the value of modal is true, then conditionally render the modal based on that truth value. But instead, you set the modal state value to true, then check its value, so it will always be true. So I would start by changing { setModal(true) ? .... => { showModal ? .... Commented Jul 27, 2020 at 18:43
  • It would be really helpful if you add a demo or a codesandbox to demonstrate what exactly you are doing Commented Jul 27, 2020 at 18:43
  • @Malcolm. Would it be possible to write that full line? I hardly use functional components except I am using props so I am kind of new to how it works.. I am trying to convert the stateful component to a stateless one so I dont get any errors. I need to use the Context API and it's only available in functional components. Commented Jul 27, 2020 at 18:49
  • @RohanNaik I am trying to convert the stateful component to a stateless one so I dont get any errors. I need to use the Context API and it's only available in functional components. Commented Jul 27, 2020 at 18:49
  • @ChinomsoJohnson That is not true. Yes useContext is build for functional components, but that doesn't mean you can't use context in class components. You can for example use Class.contextType to access the state in a class component. eg. static contextType = ProductsContext inside the class definition, then the context is available through this.context inside the class component instance methods. Check out the provided examples for some inspiration. Commented Jul 27, 2020 at 19:19

2 Answers 2

2

You shouldn't set state inside the template. This triggers re-render, what again, trigger setting state and next...

Try something like this:

import React, { useState, useContext } from 'react';

const Pizzas = () => {

    const [showModal, setModal] = useState(false);
    const [selectedPizza, setSelectedPizza] = useState(null)
    const { pizzaproducts } = useContext(ProductsContext)
    const toggleModalHandler = (p) => {
        setModal(true);
        setSelectedPizza(p)
    };
    return (
        <Aux>
            {showModal ? <Backdrop clicked={toggleModalHandler} /> : null}

            {showModal ?
                <Modal
                    clicked={toggleModalHandler}
                    ingredients={selectedPizza.ingredients}
                    price={selectedPizza.price}
                    image={selectedPizza.image}
                    name={selectedPizza.name}
                /> : null}

            <div className={styles.Pizza} id="pizza">
                <h1>Pizza</h1>
                <div className={styles.PizzaContainer}>
                    {pizzaproducts.map(p => {

                        return <div>
                            <div className={styles.PizzaCard}>
                                <div className={styles.PizzaCardHeader}>
                                    <img src={p.image} alt="pizza" />
                                    <h1>{p.name}</h1>
                                    <p>{p.ingredients}</p>
                                </div>
                                <div className={styles.PizzaCardFooter}>
                                    <h3>from ₦{p.price}</h3>
                                    <button onClick={() => toggleModalHandler(p)}>Select</button>
                                </div>
                            </div>
                        </div>
                    })}
                </div>
            </div>

        </Aux>
    )
}
export default Pizzas;
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you. The toggleModalHandler never gets passed on to the backdrop.
I fixed it. I just had to create a new handler for removing the modal.
2

You are conditionally attempting to render using setModal(true)?, which is incorrect, should be showModal?

useState returns a pair of values: the current state and a function that updates it. This is why we write const [showModal, setModal] = useState(). This is similar to this.state.showModal and this.setState in a class, except you get them in a pair.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.