Mr. Engineer
Mr. Engineer

Reputation: 375

could not find react-redux context value error triggering randomly

Here is my code for displaying the contents of my Redux store state in JSON:

   function ShowCartContents() {
      // Convert object to JSON before rendering
      const cartContentsJSON = JSON.stringify(useStore().getState())
    
      return (
        <div>
            <h2>Cart contents: {cartContentsJSON}</h2>
          </div>
      );
    }
// Works fine

Everything is working just fine but when I try to add button with onClick attribute to call another function I'm getting error:

Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>

The weird thing here is that I do have <App> wrapped in <Provider> but this error occurs and I can't find any logic why this happens. Here is the beginning of my App.js:

    import './App.css';
    import MenuItems from './components/Navbar/MenuItems';
    import ReactDOM from 'react-dom';
    import React, { useState } from 'react';
    import { createStore } from 'redux';
    import {AddItemToCart, DeleteItemFromCart, Counter} from './cart.js';
    import Header from './header';
    import Footer from './footer.js';
    import { render } from "react-dom";
    import { Provider } from "react-redux";
    import { useSelector, useStore } from 'react-redux';
    
    let store = createStore(Counter);
    const rootElement = document.getElementById("root");
    render(
      <Provider store={store}>
        <App />
      </Provider>,
      rootElement
    );
const [customerInfo, setCustomerInfo] = useState([])

Here is how I tried to add my button with OnClick for function call:

    function ShowCartContents() {
      const cartContentsJSON = JSON.stringify(useStore().getState())
    
      return (
        <div>
            <h2>Cart contents: {cartContentsJSON}</h2>
            <button onClick={(e)=> {ShowCustomerInfo(e)}}>Proceed</button>
// Also tried: <button onClick={<ShowCustomerInfo/>}>Proceed</button>
          </div>
      );
    }

This triggers the error above. Here is my ShowCustomerInfo function:

function ShowCustomerInfo(){
  fetch(`http://127.0.0.1:5000/getCustomerInfo`)
      .then(response => response.json())
      .then(response => {
          setCustomerInfo(response.result);
          console.log(response.result);
      });
      return (
        <div>
          <h2>
            {customerInfo}
          </h2>
        </div>
      );
}

What is causing the exception? Any advise would be highly appreciated.

Upvotes: 1

Views: 232

Answers (2)

Linda Paiste
Linda Paiste

Reputation: 42228

You cannot render a component from an event handler such as onClick. An event handler can update state and perform side effects like API calls but it cannot return anything. If you call components as a function instead of rendering them properly then you will get errors like "Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>".

Instead you want to update the state from your onClick handler and conditionally render different components based on the state. You could use some sort of value like a boolean state showCustomerInfo or a number state page. Since we are loading data into the array customerInfo, we can check to see if the length is greater than 0 and only show the customer info once it's not empty.

export const App = () => {
  const [customerInfo, setCustomerInfo] = useState([]);

  // use this state so that we can disable hide the "Proceed"
  // button immediately, before the fetch has completed
  const [isLoading, setIsLoading] = useState(false);

  const state = useSelector((state) => state);
  const cartContentsJSON = JSON.stringify(state);

  function loadCustomerInfo() {
    console.log("clicked");
    // can use async/await, but fetch is fine too
    setIsLoading(true);
    fetch(`http://127.0.0.1:5000/getCustomerInfo`)
      .then((response) => response.json())
      .then((response) => {
        setIsLoading(false);
        // could store this in redux instead
        setCustomerInfo(response.result);
      })
      .catch((error) => {
        console.error(error);
        setIsLoading(false);
      });
  }

  return (
    <div>
      <h2>Cart contents: {cartContentsJSON}</h2>
      <button disabled={isLoading} onClick={() => loadCustomerInfo()}>
        Proceed
      </button>
      {customerInfo.length > 0 && (
        <div>
          <h3>Customer Info</h3>
          <p>{JSON.stringify(customerInfo)}</p>
        </div>
      )}
    </div>
  );
};

Upvotes: 1

Furkan Kalaycioglu
Furkan Kalaycioglu

Reputation: 66

There are a few anti-patterns I see in your code like:

  1. Your App.js creates a state which is outside of your entire component tree as you do

    const [customerInfo, setCustomerInfo] = useState([])

    after creating the rootElement and using it in ReactDOM.render. You must carry this state into an actual component as any state must be part of React dataflow but what you do actually violates this.

    After refactoring that part you can use this state in your ShowCustomerInfo but you should call it as <ShowCustomerInfo/> instead of ShowCustomerInfo() as it renders JSX and again you should do this, because you want to make this component part of React dataflow otherwise you are just treating it just like a regular function.

  2. This is just a naming convention but your App.js file doesn't define <App/> component but just renders it. I would expect your App.js to be index.js as it's going to be the beginning of your virtual-dom with ReactDOM.render.

If you make changes considering first part, you'll get rid of the problem I believe.

Upvotes: 1

Related Questions