I'm having this error message:
Cannot read properties of undefined (reading 'params') TypeError: Cannot read properties of undefined (reading 'params') at Function.mapStateToProps [as mapToProps]
I read that I should use useParams
, but I couldn't adapt it to my code. I use match.params
, history
codes. How can I update this codes for react-router-dom@6
?
const [product, setProduct] = useState({ ...props.product });
useEffect(() => {
if (categories.length === 0) {
getCategories()
}
setProduct({ ...props.product });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.product]);
export function getProductById(products, productId) {
let product = products.find(product => product.id === productId) || null;
return product;
}
function mapStateToProps(state, ownProps) {
const productId = ownProps.match.params.productId;
const product = productId && state.productListReducer.length > 0
? getProductById(state.productListReducer, productId)
: {}
return {
product: product,
products: state.productListReducer,
categories: state.categoryListReducer
}
}
And my routes:
<Routes>
<Route path="/" element={<DashBoard></DashBoard>}></Route>
<Route path="/cart" element={<CartDetails></CartDetails>}></Route>
<Route
path="/saveproduct/:productId"
element={<AddOrUpdateProduct></AddOrUpdateProduct>}>
</Route>
</Routes>
I try use useParams
but I have this error this time:
React Hook
useParams
is called in functionmapStateToProps
that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use" react-hooks/rules-of-hooks
function mapStateToProps(state, ownProps) {
const { productId } = useParams();
const product = productId && state.productListReducer.length > 0
? getProductById(state.productListReducer, productId)
: {}
You can only use React hooks in React functions and custom React hooks. The useParams
hook cannot be called within the mapStateToProps
function. Using the connect
Higher Order Component is no longer the preferred method of subscribing React components to the app's Redux store. Use the useDispatch
and useSelector
hooks in the component to access the dispatch
function and select slices of state. You'll move the logic to retrieve a product into the component as well.
Example:
import { useParams } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
export function getProductById(products, productId) {
return products.find(product => product.id === productId) || {};
}
...
const AddOrUpdateProduct = () => {
const dispatch = useDispatch();
const { productId } = useParams();
const products = useSelector(state => state.productListReducer);
const categories = useSelector(state => state.categoryListReducer);
useEffect(() => {
if (!categories.length) {
dispatch(getCategories());
}
}, [categories]);
const [product, setProduct] = useState(() => getProductById(products, productId));
useEffect(() => {
setProduct(getProductById(products, productId));
}, [products, productId]);
...
};
Since it's not clear what the exact use case is here for the derived product
state, I'll just inform you that generally speaking the above is considered a React anti-pattern. In most cases if you see that you've coded a useState
|useEffect
coupling you should instead use the useMemo
hook.
Example:
const product = useMemo(() => {
return getProductById(products, productId);
}, [products, productId]);
There are few exceptions to this anti-pattern, for example one being if you are populating some local state that is used in a form and the user is editing the local copy which will later be submitted to update the source data in the store.