How to destructure an Array from an Object in Redux React? - redux

I am using redux and I want to destructure teamMembers - Array from an Object name - teamData which I am getting from REDUX. This is what I have done below. I just want to confirm whether is this the right way to do.
const teamData = useSelector((state) => state.team.teamData);
const { description, heading } = teamData;
const teamMembers = useSelector((state) => state.team.teamData.teamMembers);

If possible, you should not do
const teamData = useSelector((state) => state.team.teamData);
const { description, heading } = teamData;
Your code here is interested in description and heading, but your useSelector selects the whole teamData.
Now, if there is a third property on teamData, let's say winStreak and that winStreak changes, your component will rerender. Because that change to winData.teamStreak caused winData to change. And your useSelector is watching winData for changes.
If you instead do
const description = useSelector((state) => state.team.teamData.description);
const heading = useSelector((state) => state.team.teamData.heading );
your component will only rerender, when description or heading change. Because your useSelector now watches those two properties for changes. It does not care if teamData changes, it only cares if teamData.descriptionorteamData.heading` change.
So, in your case you should do
const description = useSelector((state) => state.team.teamData.description);
const heading = useSelector((state) => state.team.teamData.heading );
const teamMembers = useSelector((state) => state.team.teamData.teamMembers );
and not use any destructuring here.
If you have to destructure the result of a useSelector call, that likely means that your useSelector call is also observing data you are not interested in in the first place.

Related

pass updated states (bunch of them) from child to parent? React Native Firebase?

So I update the existing DB(created on RegisterScreen) => registered user goes to HomeScreen/ProfileScreen(empty right now) => user, goes to update their profile on EditScreen => update was successful and logged in user returns back to HomeScreen(where new values should be displayed).
Here is the question. How do I display all values (or just a few chosen ones) that got updated from EditScreen back on the ProfileScreen?
Here is the EditScreen code:
//imports here....
export default function EditScreen() {
const[getFoodCheckboxState, getFoodSetCheckboxState] = useState();
const [getFoodText, setGetFoodText] = useState(false)
const [importantGetFoodInfo, setImportantGetFoodInfo] = useState('')
const [getFoodAreas, setGetFoodAreas] = useState('')
const [giveFoodCheckboxState, giveFoodSetCheckboxState] = useState(false);
const [giveFoodText, setGiveFoodText] = useState('')
const [importantGiveFoodInfo, setImportantGiveFoodInfo] = useState('')
const [giveFoodAreas, setGiveFoodAreas] = useState('')
const {getUser} = useShared();//custom hook
const handleInputChange = async () => {
const timestamp = firebasestorenotfunc .FieldValue.serverTimestamp()
const wantsToGetFood ={
ActivityStatusGet: getFoodCheckboxState,
createdAt: timestamp,
getFood: getFoodText,
importantGetFoodInfo: importantGetFoodInfo ,
GetFoodMeetingArea: getFoodAreas,
}
const wantsToGiveFood={
ActivityStatusGive: giveFoodCheckboxState ,
createdAt: timestamp,
giveFood: giveFoodText,
importantGiveFoodInfo: importantGiveFoodInfo ,
GiveFoodMeetingArea: giveFoodAreas,
};
const snapshot = await new Promise((resolve, reject) => {
const usersRef = db.collection('users');
const user = getUser();
usersRef.doc(user.uid).update({ wantsToGetFood, wantsToGiveFood });
navigation.replace("Home")//back to home/profile page where i need to //display all these updated data somehow...
});
resolve(snapshot);
}
return(
//all input fields, update button, etcc...
)
I could not figure out how to useContext for these (i use it for functions in useShared.js but cant do it with anything else)
cant figure out how to pass it as props from all other tutorials, and useContext tutorials since it doesnt see, simular enough to me, or namig convention is confusing me.
I'd appreciate it if someone can show me how to achieve what I want, with so many values

NGRX selectors: factory selector within another selector without prop in createSelector method

Using the factory selector pattern const selectA = (id: number) => createSelector(...) I have an instance where I want to reuse this selector within another selector (that iterates through an array of IDs) but I don't know the value to pass into the factor selector when calling createSelector.
So I have a selector that I use whenever I want to get a slice of the state for component A.
const selectA = (id: number) =>
createSelector(
selector1.selectEntityMap,
selector2.selectEntityMap,
selector3ById(id),
(
thing1,
thing2,
thing3
) => {
return ...
});
Now I want to get a list of component A for each item in an array.
const selectListOfA = (ids: number[]) =>
createSelector(
selectA,
(selectorA) => {
return ids.map((id) => selectorA(id));
});
The problem is selectA, which is now a factory selector, expects a parameter, but I don't know it when calling createSelector.
I can get the code to compile by creating another factory onto of the factory
const selectAFactory = () => selectA;
And then reference the new factory in the createSelector
const selectListOfA = (ids: number[]) =>
createSelector(
selectAFactory, <<< here
(selectorA) => {
return ids.map((id) => selectorA(id));
});
But of course, what's now happening is the selector is returning a list of MemoizedSelector[].
This pattern doesn't seem like it should be this complicated, are people not reusing their selectors in this way, what am I missing?
The function returned by selectA is a standard function, ie nothing magical about it, as explained well in this blog post: https://dev.to/zackderose/ngrx-fun-with-createselectorfactory-hng
This means selectListOfA can simply call the function returned from selectA for each id and an array of the state slices for component A will be returned:
export const selectListOfA = (ids: number[]) =>
createSelector(
(state) => state,
(state) => ids.map((id) => selectA(id)(state))
);
This works as expected but since the projector function will be executed every time anything in the store changes (recreating the selector for every id) this solution will have major performance issues.
We could just as well simplify the code to this with equally poor performance:
const selectListOfA = (ids: number[]) =>
(state) => ids.map(
(id: number) => selectA(id)(state)
);
If we instead supply an array of selectors as input to the createSelector call then Ngrx will be able to correctly determine when it has to reevaluate the selectA selectors:
const selectListOfA = (ids: number[]) =>
createSelector(
ids.map((id) => selectA(id)), // This results in an array of selectors
(...resultArr) => resultArr
);
However, Typescript will complain since the createSelector method does not have a matching overload declared for an array of variable length so we need to loosen up the input type of the array (to any) as well as specify the return type of selectListOfA.
The answer to the question is thus:
const selectListOfA = (ids: number[]) =>
(createSelector(
ids.map((id) => selectA(id)) as any,
(...resultArr) => resultArr
) as (state) => string[]);

Pass a whole object to redux reselect selector, but change it only if one property of the object changes

Started working with Reselect and there's one thing I can't seem to find an answer for.
Say I have a helper fn (getVehicleList) which does some heavy calculations, so I don't want it to re-run too much. I use state selectors to get the properties I need, something like:
const getVehicles = (state) => state.vehicles.vehicles;
const getVehicle = (state) => state.vehicles.vehicle;
const getUserId = (state) => state.auth.user.id;
I then have implemented the createSelector:
const getVehicles = createSelector(
[getVehicles,
getVehicle,
getUserId],
(vehicles, vehicle, id) => getVehicleList(
vehicles,
vehicle,
id,
),
);
Now, vehicle returns an object with multiple fields. If any of these fields change, the object changes and so everything is recomputed again. Is there a way to stop this recomputing until the id and only the id of the vehicle changes?
I tried doing a state selector for the id, like
const getVehicle = (state) => state.vehicles.vehicle.id;
But that doesn't work for me, cause I need the whole vehicle object inside my helper fn and not just the id.
Thanks in advance for the help!
You can try the following:
const getVehicles = (state) => state.vehicles.vehicles;
const getVehicle = (state) => state.vehicles.vehicle;
const getUserId = (state) => state.auth.user.id;
const selectVhicleId = createSelector(
[getVehicle],
({ id }) => id //return only the id
);
const selectVehicles = createSelector(
[getVehicles, selectVhicleId, getUserId],
(vehicles, vehicleId, id) =>
getVehicleList(vehicles, { id: vehicleId }, id)
);
Here is some information about how I use reselect with React.
Here is an example that re calculate vehicles when vehicle.id changes (or any of the other dependencies). It will not re calculate if other values of vehicle change so the vehicle used getVehicleList gets a stale vehicle passed to it that is only refreshed when vehicle.id changes:
const getVehicles = (state) => state.vehicles.vehicles;
const getVehicle = (state) => state.vehicles.vehicle;
const getUserId = (state) => state.auth.user.id;
const createSelectVehicles = (vehicle) =>
createSelector([getVehicles, getUserId], (vehicles, id) =>
getVehicleList(vehicles, vehicle, id)
);
const Component = () => {
//only re calculate vehicle if vehicle.id changes
const vehicle = useSelector(
getVehicle,
(a, b) => a?.id === b?.id
);
//only create the selector when vehicle changes
// vehicle only changes when vehicle.id changes
const selectVehicles = React.useMemo(
() => createSelectVehicles(vehicle),
[vehicle]
);
//vehicles is re calculated when vehicle.id changes
// or when state.vehicles.vehicles changes or
// when state.auth.user.id changes
const vehicles = useSelector(selectVehicles);
};

Redux Toolkit -selectById

I tried to use selectbyId helper method of reduxtoolkit without a success can anyone help me with this
Category Slice
export const {
selectAll:selectCatetories,selectById:selectCategoryById
}=categoriesAdapter.getSelectors(state=>state.inventory.categories.categories)
Category Component
const category=useSelector((id)=>selectCategoryById(id))
I am sure there is something wrong with my implementation
To get your category with an Id, you will need to create a custom selector with reselect
import { createSelector } from '#reduxjs/toolkit';
const { selectById } = categoriesAdapter.getSelectors();
export const getCategoriesState = (rootState) => rootState.categories; // I am assuming you have a 'categories' reducer in your redux store.
export const selectCategoryEntity = (id) => {
return createSelector(getCategoriesState, (state) => selectById(state, id));
}
Then, you will be able to use the selector hook in your React component:
const category = useSelector(selectCategoryEntity(id));
You just need to pass the state to the useSelector function.
const category = useSelector((state)=>selectCategoryById(state, id))

can't unsubscribe on my ngrx selector call

I have the following code
selectProduct(id){
const sub = this.store$.select(ProductStoreSelector.selectProductByID(id)).subscribe((product) => {
this.store$.dispatch(new ProductStoreAction.SetSelectedProduct({productID: product.id}));
sub.unsubscribe();
});
}
Basically, I would like to get my list of product, and get one by ID, then change my store state so that the selectedProduct become the one I just selected
export const featureAdapter: EntityAdapter<IProduct> = createEntityAdapter<IProduct>({
selectId: model => model.id,
});
export const selectAllProducts: (state: object) => Array<IProduct> = featureAdapter.getSelectors(selectProductsState).selectAll;
export const selectProductByID = (id: string) => createSelector(
selectAllProducts,
(products) => products.find((product) => product.id === id)
);
and my store is an entityState of products with one selected
export interface State extends EntityState<IProduct> {
selectedProduct: IProduct;
}
but the problem is,
althougt I do get my productId back, I can't unsubscribe to sub.unsubscribe() because it is undefined.
You can use either take(1) to listen for values only one time. Otherwise try to unsubscribe like below:
selectProduct(id){
this.store$.select(ProductStoreSelector.selectProductByID(id)).subscribe((product) => {
this.store$.dispatch(new ProductStoreAction.SetSelectedProduct({productID: product.id}));
}).unsubscribe();
}
using take(1):
selectProduct(id){
this.store$.select(ProductStoreSelector.selectProductByID(id))
.take(1)
.subscribe((product) => {
this.store$.dispatch(new ProductStoreAction.SetSelectedProduct({productID: product.id}));
});
}

Resources