Reputation: 29
So, this is my first time using Hooks and also be aware that Im learning please. Im currently working on a group project were only I was allowed to use React Hooks, we are working on a front-end project. I'll try my best to detail everything. We have an App class component were we hold default state currentProduct: ''
. We use componentDidMount()
to get data from an API and then update the state to be by default, the first product from an Array of Objects.
I created a separate component called RelatedItems
, where Im using hooks.
This is how my component looks like:
import React, { useState, useEffect } from 'react';
const RelatedItems = (props) => {
const [items, setItems] = useState(props.currentProduct);
useEffect(() => {
setItems(props.currentProduct);
}, [items])
return (
<div className="Related-Items">
<h1>Related Items</h1>
<h3>{items}</h3>
</div>
)
}
export default RelatedItems;
I tried using useEffect
looking at the Docs and some other articles in StackOverflow, but I still keep my items state as an empty string. I figured it's happening because essentially componentDidMount
is an async function.
This is how the App component looks like:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
currentProduct: ''
}
this.handleClick = this.handleClick.bind(this);
}
handleClick(e, id) {
e.preventDefault();
this.setState({ currentProduct: Number(id) })
}
componentDidMount() {
axios.get('/sedna/products')
.then((response) => {
this.setState({
currentProduct: response.data[0].id
})
})
.catch((error) => {
console.error(error);
})
}
render() {
return (
<div>
<h1>Hello Sedna</h1>
<SearchBar handleClick={this.handleClick} />
<RelatedItems currentProduct={this.state.currentProduct} />
</div>
)
}
}
export default App;
I'm rendering items in an H3 just to test that I get the proper state, any feedback is welcome! Sorry any grammar mistakes
Upvotes: 0
Views: 137
Reputation: 12909
Your current code will result in an infinite loop if the useEffect ever fires because the dependency is changed by the useEffect
.
Instead you want the dependency to be the passed prop, so that when the prop changes the useEffect
is triggered and the state is updated appropriately.
const RelatedItems = (props) => {
const [items, setItems] = useState(props.currentProduct);
useEffect(() => {
setItems(props.currentProduct);
}, [props.currentProduct])
return (
<div className="Related-Items">
<h1>Related Items</h1>
<h3>{items}</h3>
</div>
)
}
Keep in mind though that setting state from props is a bit of an anti-pattern because it fragments the state so there is no longer a single source of truth. (If you change items
in RelatedItems
the parent will hold a different value)
For this reason you are better off handling state management in the parent and passing props to the child for rendering.
const RelatedItems = (props) => {
return (
<div className="Related-Items">
<h1>Related Items</h1>
<h3>{props.currentProduct}</h3>
</div>
)
}
Upvotes: 1
Reputation: 266
If you want to duplicate data inside the component state, you should specify props.currentProduct
as a hook dependency.
...
useEffect(() => {
setItems(props.currentProduct);
}, [props.currentProduct])
...
But in the code that you have given, the RelatedItems
component does not need an own state at all. In any case, it will be re-render when the prop changes
const RelatedItems = (props) => {
return (
<div className="Related-Items">
<h1>Related Items</h1>
<h3>{props.currentProduct}</h3>
</div>
)
}
Upvotes: 1