Reputation: 454
I am trying to add another property to the array of object coming from the api.
const productList = useSelector((state) => state.productReducer.productList);
if (productList !== undefined) {
for (let b = 0; b < productList.length; b++) {
productList[b].power = 0;
}
}
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
async function fetchData() {
setLoading(true);
await dispatch(fetchProductList());
setLoading(false);
}
fetchData();
});
return unsubscribe;
}, [navigation]);
As you can see I am getting data by using using useDispatch, useSelector
hooks.
But What I am doing now is looping the product list and adding power property to each individual product.
This thing is that this process is recalled every rendered.
If I changed the productList[4].power = 4
in jsx.
This loop is called again and the data is gone back to 0.
if (productList !== undefined) {
for (let b = 0; b < productList.length; b++) {
productList[b].power = 0;
}
}
Where should I put this code so that It will not re-rendered. For my case I trying putting this loop in useEffect but the initial data is empty array so it won't work
Upvotes: 0
Views: 525
Reputation: 355
It would help if you provided the whole file. It's difficult to see exactly what the JSX is referencing and changing however....
I can see that you're dispatching an action to get some data which is then stored in your redux store and brought into the component with the useSelector hook.
Now first things first... It is possible that data mutations can occur when using useSelector hook, meaning if you're not handling immutability then changes to this state object from a component could be modifying the data in your store.
Regardless of this, your current implementation shows that regardless of what is in the store, after you get the products from the store, you are setting every power to 0 having that loop beneath your selector hook. So whatever you do it will always show as 0.
Perhaps in your reducer when saving the products I would to something like below. Where data is your product list api response...
case 'FETCH_PRODUCT_LIST_SUCCESS':
const newState = _.clone(state);
newState.productList = data.map(product =>
if (product.power === undefined || product.power === null) {
product.power = 0;
}
return product;
);
return newState;
For the useSelector hook, I would highly recommend creating functions that take state as a parameter which returns a copy of your data using a library like 'lodash'. In the example below you can see that I'm using the .clone function on lodash which ensures that the data passed to your component will not carry the same reference. Meaning your components will not be able to modify the data in the store.
import _ from 'lodash-es';
export const getProductList = (state) => _.clone(state.productReducer.productList);
Then use in your selector hook like so:
const productList = useSelector(state => getProductList(state));
This will help to keep your code clean and easy to understand.
For the rest of your task, there are many ways that you can approach this but I'll give you two examples.
Create an action for updating a product's power which will be dispatched by the component when the power input changes. This will persist the change in the redux store. In your reducer you would add a case statement for this new action and modify the data as you wish. You may even want to persist this change in a database somewhere and instead make an api call to save a product's power and then modify your store's product list with the newly persisted power.
If you only want to persist these changes locally in the component use can make use of the useState hook. You could initialise a useState hook with the products from your product list selector and write a handler to update this local state.
Option 1 would look like this example below. Where index and power would be passed in your updateProductPower action's payload.
case 'UPDATE_PRODUCT_POWER':
const newState = _.clone(state);
newState.productList[index].power = power;
return newState;
Option 2 would look like this:
const productList = useSelector((state) => _.clone(state.productReducer.productList));
const [products, setProducts] = useState(productList);
// Updates your local state if the store changes. But will overwrite changes in your local state.
useEffect(() => {
setProducts(productList);
}, [productList]);
// Dispatches an action to persist changes on API when the local products state changed.
useEffect(() => {
dispatch(persistProductList(products);
}, [products]);
const handlePowerChange = (index, power) => {
const result = _.clone(products);
result[index].power = power;
setProducts(result);
}
You can use a mixture of both but you just need to remember how data flows in a redux/flux architecture. Sorry not a perfect answer but should help you understand the direction you need to go. I have to crack on with work now.
Upvotes: 1
Reputation: 5937
You can try the split operator:
if (productList !== undefined) {
for (let b = 0; b < productList.length; b++) {
productList[b] = {...productList[b], power: 0};
}
}
Or you can assign via array brackets:
if (productList !== undefined) {
for (let b = 0; b < productList.length; b++) {
productList[b]["power"] = 0;
}
}
Upvotes: 0