Reputation: 71
I am basically making a React app that imitates Craigslist. The logged in user can view components that are items for sale or services.
The user can click on the component to get details, which also renders a comment form. When the form is submitted, the Sale object property 'comments' is updated. But since the reference is not updated, useEffect() does not trigger a re-render. The new comment is displayed with manual refreshing.
I know I probably need to create a new reference with .map() and assign the state to the new reference but have not been able to successfully get it right.
The hook returns an object like this (with the 'comments' array of objects inside):
{
"sale": {
"itemName": "Legos",
"itemInfo": "Dont step on them!",
"price": 33,
"postedBy": testUser,
"likedBy": null,
"createdAt": "2021-06-15T13:20:16.508Z",
"comments": [
{
"commentText": "How many?",
"postedBy": "davey",
"createdAt": "2021-06-17T16:52:55.926Z",
"id": 39
},
{
"commentText": "Could you come down on price?!",
"postedBy": "testUser2",
"createdAt": "2021-06-17T17:14:48.155Z",
"id": 40
}
]
}
}
Here is my code that calls useEffect():
function SalesDetail() {
const { id } = useParams();
const currentUser = useContext(UserContext);
const route = 'sales'
const [sale, setSale] = useState()
useEffect(function getSaleById() {
async function getSale(){
let sale = (await BoonelistApi.getSale(id))
setSale(sale)
}
getSale();
}, [id]);
I've tried making the sale.comments a dependency in the hook, but still can't seem to wrap my head around it and the 'Don't mutate state' adage. I've looked at similar problems here on Stack as well but have not been able to get it to work for me.
Any help greatly appreciated and please let me know if more details are needed.
Upvotes: 3
Views: 5047
Reputation: 1186
You can use Provider Pattern for re-rendering. Follow the below lines of code I guess it will help you a little
// Provider.js
const SaleContext = createContext({
sale :{},
getSale :()=>{},
setSaleId :()=>{}
});
const SaleProvider = ({children})=>{
const [id,setId] = useState('') // string or number
const [sale,setSale] = useState({})
const getSaleById = async (id) =>{
let sale = (await BoonelistApi.getSale(id));
return sale;
}
const getSale = (id) => setSale(getSaleById(id));
const setSaleId = id => setId(id);
useEffect(()=>{
getSale(id);
},[id])
return (
<SaleContext.Provider values = {{
sale,
getSale,
setSaleId
}}>
{children}
</SaleContext.Provider>
)
}
export default SaleProvider;
function SalesDetail() {
const [saleList, setSale] = useState();
const { id } = useParams();
const {sale,getSale,setSaleId} = useContext(SaleContext);
const currentUser = useContext(UserContext);
const route = 'sales'
const getSaleById = () => {
setSaleId(id);
setSale(sale)
} // every time this function gets called the useEffect will trigger in the provider.js
<SaleProvider>
App.js
</SaleProvider>
More on Provider Pattern. I guess this helped you a little. Also, you can use Redux or Saga, to manage your states.
Upvotes: 2
Reputation: 61
It looks like you're rendering off of the 'sale' state property. React will not detect an updated state and re-render when there is a change within a nested state object (in this case the 'comments' property). There are many structures you could go with to get the re-render you want, just keep in mind that a change in the props of a component will always trigger a re-render and state is best used as flat un-nested key:value pairs. I would create a 'comments' component for each sale item and pass the data in via a prop from the parent sale item <Comments comments={sale.comments}/>
.
Upvotes: 1