Reputation: 375
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
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
Reputation: 66
There are a few anti-patterns I see in your code like:
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
instead of <ShowCustomerInfo/>
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.
This is just a naming convention but your App.js
file doesn't
define
component but just renders it. I would expect
your <App/>
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