Adding Redux State Values - reactjs

I'm trying to combine two separate prices into one total price. I have the two individual price states stored and correctly updating using independent reducers, but I need a way to combine these two dynamic values into a third, total price state.
import React, {useState} from "react";
import { useSelector, useDispatch } from "react-redux";
const planPrice = useSelector(state => state.planPrice);
const repairPrice = useSelector(state => state.repairPrice);
//Use the state hook to store the Total Price
const [totalPrice, setTotalPrice] = useState(0);
const [items, setItems] = useState([]);
function addItem (newItem) {
setItems((prevItems) => {
return [...prevItems, newItem];
});
//Update the Plan Price
if ({plan}.plan === "Premium") {
dispatch(addItemPremium());
} else if ({plan}.plan === "Basic") {
dispatch(addItemBasic());
}
//Update the Repair Price
if (newItem === "Small Chip") {
dispatch(addSmallChip());
} else if (newItem === "Big Chip") {
dispatch(addBigChip());
}
// //Update the Total Price
setTotalPrice({planPrice} + {repairPrice});
}
Is this even possible using Redux, or am I going about this the wrong way?

If you have derived state, you should consider using selectors. These are functions that take state as an argument and return a derived state value.
For example:
function totalPrice(state) {
return state.price1 + state.price2;
}
If you need to use this in mapStateToProps, you can do so as follows:
const mapStateToProps = state => ({
price: totalPrice(state)
})
If you're using hooks, you can use the useSelector hook:
const price = useSelector(totalPrice);
There are libraries (e.g., reselect) that will help you create composable selectors with caching for efficiency.
Note that the reason you should use selectors versus storing redundant state, as you may have intuited, is that redundant state can get out of sync and then you're hosed. But if you have a function that computes the total price, it'll always be in sync with the individual state parts.

Related

dom is not updated after state change via Context API

I am quite new to React and tried to finish a project on FrontendMentor. It's basically a simple OPA of an online store with just one product and the ability to add this product to a cart.
I thought it would be a good idea to learn about Reacts Context API so that I get a basic understanding.
The first context that I created basically looks like this:
import { createContext, useContext, useState } from "react";
export const CartContext = createContext();
export const CartProvider = ({ children }) => {
const cartValue = useCartProvider();
return (
<CartContext.Provider value={cartValue}>{children}</CartContext.Provider>
);
};
export const useCart = () => {
return useContext(CartContext);
};
export const useCartProvider = () => {
const initialValue = [{name: name, price: price, amount: amount}];
const [cartAmount, setCartAmount] = useState(initialValue);
const addToCart = (name, price, amount) => {
setCartAmount([
...cartAmount,
{ name: name, price: price, amount: amount },
]);
};
return {
cartAmount,
setCartAmount,
addToCart,
};
};
When I add something to the Cart, I want to display the current amount of products in the cart with a little number above the cart. I implemented everything to the point that I see the number and can access the state variable cartAmount in the Navbar component.
Unfortunately it does not update. When I add to the cart I can console.log the event but the badge above the cart is not being updated.
Also - the update does not happen in real time.
I've read about Reacts ability to badge state updates for performance reasons and I also read about using useReducer hook for the actions.
With my current knowledge I feel like I cannot make the connection between all of these elements, yet.
Would like your help on that - please let me know if you need more information than provided.
Thanks in advance.

Reusing a variable that is based on multiple redux props without duplication of code

I want to set a property based on multiple values from my redux store. (If it makes any difference I use Rematch to handle my global store)
In my case, a boolean property called showSidebar which is only true if several other conditions are met - as a simplified example: loggedIn === true and userProjects.length > 0.
The logic is fine, e.g.
const showSidebar = loggedIn && userProjects.length > 0;
...but I use this in lots of components, and every time I have to import all of the dependent properties and include the conditional logic.
How can I avoid needless duplication of logic and imported props?
I was thinking the best way may be to write my own useShowSidebar hook, which would look something like the following:
import { useState, useEffect } from 'react';
import { useSelector } from 'react-redux';
const useShowSidebar = () => {
const loggedIn = useSelector((models) => models.model.loggedIn);
const userProjects = useSelector((models) => models.model.userProjects);
const [showSidebar, setShowSidebar] = useState(false);
useEffect(() => {
setShowSidebar(loggedIn && userProjects.length > 0);
}, [loggedIn, userProjects]);
return showSidebar;
};
export default useShowSidebar;
Would this be a sensible and efficient way to achieve this without any issues? Or is there a better way? I assume this must be a common scenario.
I feel like there should be a way to have the property showSidebar in the global store itself as a property that is dependent upon other properties, and just import this one property into my components (using either mapStateToProps or the more modern useSelector hook, but I can't figure out if this is possible.
Your use of useState causes an unneccessary rerender. Just write it all as one selector.
const useShowSidebar = () => {
const showSidebar = useSelector((state) =>
state.model.loggedIn && state.model.userProjects.length > 0
);
return showSidebar;
};
Or move it into a selector function and use that in your components:
const selectShowSidebar = (state) =>
state.model.loggedIn && state.model.userProjects.length > 0;
// in your component:
const showSidebar = useSelector(selectShowSidebar)

I'm confused about the arguments that i passe to a selector function that uses reselect inside containers

I'm learning the usage of reselect, but i'm confused about the way i make use of the memoized selector in inside my container.
I've got a component called Summary that received 3 props thats are subtotal, tipAmount , total .
It looks like this export const Summary = ({ subtotal = 0, tipAmount = 0, total = 0 })=>{...}.And those props are being inject my the connect HOC inside a dedicated container that i've called SummaryContainer with the following code.
import { connect } from 'react-redux';
import { Summary } from '../components/Summary';
import {
selectTipAmount,
selectSubtotal,
selectTotal
} from '../store/items/selectors';
const mapStateToProps = (state) => {
const subtotal = selectSubtotal(state);
const tipAmount = selectTipAmount(state);
const total = selectTotal(state);
return {
subtotal,
tipAmount,
total
};
};
export const SummaryContainer = connect(mapStateToProps)(Summary);
As you can see this code uses 3 selectors selectTipAmount , selectSubtotal and selectTotal that i'm importing from a selector file with the follwing code.
import { createSelector } from 'reselect';
const selectItems = (state) => state.items;
const selectTipPercentage = (state) => state.tipPercentage;
export const selectSubtotal = createSelector([selectItems], (items) =>
items.reduce((acc, { price, quantity }) => acc + price * quantity, 0)
);
export const selectTipAmount = createSelector(
[selectSubtotal, selectTipPercentage],
(subtotal, tipPercentage) => subtotal * (tipPercentage / 100)
);
export const selectTotal = createSelector(
[selectSubtotal, selectTipAmount],
(subtotal, tipAmount) => subtotal + tipAmount
);
export const selectItemWithTotal = createSelector([selectItems], (items) =>
items.map((item) => ({ ...item, total: item.price * item.quantity }))
);
export const selectItem = (state, props) =>
state.items.find((item) => item.uuid === props.uuid);
export const selectItemTotal = createSelector(
[selectItem],
(item) => item.price * item.quantity
);
So here is my problem :
When i call write const total = selectTotal(state);inside mapStateToProps i passe to the total constant the results of the execution of selectTotal(state) selector and when i look at the argument, it's state.It looks like i'm invoking the selectTotal selector by passing and state argument, but in the actuel implementation it isn't a function that takes a state param.In the actual implementation that selection equals the result of createSelect function like this:
export const selectTotal = createSelector(
[selectSubtotal, selectTipAmount],
(subtotal, tipAmount) => subtotal + tipAmount
);
I haven't seen where the state argument is being passed.What is going on?
The code works perfectly but i'm lost about where the state argument is being used.
When i look at selectItems selector it makes sens, but with selectors that are created with createSelector i'm lost.
I find it easiest to think of reselect selectors as sort of the "opposite" of redux reducer functions. With reducer functions each reducer owns a chunk of state and when they are combined they form a reducer tree where once they are all combined form the entire state object.
The selectors are basically doing the opposite. They start with simple selectors that take the entire state object and pluck out chunks of it. These sub-state chunks can then be fed into created selectors to pluck out smaller chunks (or compute derived state). The inputs used in createSelector are a sort of "dependency", when one of them updates then the selector recomputes its value.
createSelector is essentially a higher order function that consumes "dependencies" and a result function, and returns a function that consumes the state object. When you call selectSubtotal and pass state you are really passing state to a selector tree (or set of trees).
For example, when state.items updates the selectItems selector that is an input to selectSubtotal will update its return value. This update will trigger selectSubtotal to then recompute its value, and so on for any other selectors that use selectSubtotal as an input.
If you are curious, the resultant selector function will also have a resultFunc property that matches the computing function originally passed to createSelector. For example, selectTipAmount.resultFunc will be (subtotal, tipPercentage) => subtotal * (tipPercentage / 100), called like selectTipAmount.result.Func(subTotal, tipPercentage);. I use this heavily when unit testing my selectors since I really don't care about memoizing a value, but rather that they correctly compute derived state; I won't need to mock up an entire state object that matches the selector dependencies.
The createSelector function returns a function so in const mySelectorFunction = createSelector... the value mySelectorFunciton is a function.
A simple implementation of createSelector without memoization would be:
const createSelector = (functions=[],calculatorFunction) => {
// you could do memoize(calculatorFunction) to implement memoization
//the function returned is the selectOnePlusTwo in demo below
return (...args) =>
// when this function is called then it will call functions with args
// and pass the returning values to calculatorFunction
calculatorFunction(...functions.map(fn=>fn(...args)))
}
// demo createSelector
const state = { one:1,two:2 }
const selectOne = state=>state.one
const selectTwo = state=>state.two
const selectOnePlusTwo = createSelector(
[selectOne,selectTwo],
(one,two)=>one+two
)
console.log(
'one plus two:',
selectOnePlusTwo(state)
)
Memoization is a nice touch that could make your code slightly more performant by skipping virtual DOM compare but what usually causes havoc on your performance is passing in a new handler reference that will cause this comparison to fail and make React re paint that DOM, for example <SomeComponent onClick={()=>newRefCreated} /> will re paint whatever SomeComponent generates because the onClick changes every time. This is why there is a useCallback hook.
I'd say that the power of createSelector comes from easily re usable code and preventing duplicate implementation. If in the example above state.one would change to a string I need to only refactor the selectOne function and all other selectors depending on state.one would still work.

useState change values in reducer REACT JS

I stored an object in the reducer, I retrieve using useSelector <definitely :) > it and I use as the initial value for useState as bellow
const Product = useSelector(state => state.reducer.selectedMeal);
const [SelectedMeals, setSelectedMeals] = useState({...Product});
and I have this function to change the quantity which is one of the attributes
const Increase = () => {
let copy = {...SelectedMeals};
copy.featuresList.items.qty += 1
setSelectedMeals(copy);
}
ISSUE:
the value in Product.featuresList.items.qty //changed as well
State Mutation
You are mutating state by doing this because the featuresList inside of copy is the same as the one in the SelectedMeals state and the one is the Product in the reducer. You are dealing with a shallow copy.
Minimize Component State
Your Product variable will subscribe to the latest changes in your redux store.
You only need local component state for data that you don't want to push to the redux state, such as form inputs or other UI data.
I would advise that your useState hooks (you can have multiple) should store the minimum amount of data that you need to describe the local changes. You probably don't need to copy the whole Product.
Here's an example where the only local state is a number of the current quantity.
const [qty, setQty] = useState(Product.featuresList.item.qty);
const increase = () => {
setQty( prev => prev + 1 );
}
Nested Updates
Updating a state property that's three-levels deep without mutation is really annoying, but you can do it:
const increase = () => {
setSelectedMeals((product) => ({
...product,
featuresList: {
...product.featuresList,
items: {
...product.featuresList.items,
qty: product.featuresList.items.qty + 1
}
}
}));
};

What are selectors in redux?

I am trying to follow this code in redux-saga
export const getUser = (state, login) => state.entities.users[login]
export const getRepo = (state, fullName) => state.entities.repos[fullName]
Which is then used in the saga like this:
import { getUser } from '../reducers/selectors'
// load user unless it is cached
function* loadUser(login, requiredFields) {
const user = yield select(getUser, login)
if (!user || requiredFields.some(key => !user.hasOwnProperty(key))) {
yield call(fetchUser, login)
}
}
This getUser reducer (is it even a reducer) looks very different from what I would normally expect a reducer to look like.
Can anyone explain what a selector is and how getUser is a reducer and how it fits in with redux-saga?
getUser is not a reducer, it is indeed a selector, that is, a function that knows how to extract a specific piece of data from the store.
Selectors provide an additional layer such that if you altered your store structure and all of a sudden your users were no longer at state.entities.users but instead at state.users.objects.entities (or whatever) then you only need to update the getUser selector and not every place in your app where you were making a reference to the old location.
That makes them particularly handy when it comes to refactoring your Redux store.
Selectors are getters for the redux state. Like getters, selectors encapsulate the structure of the state, and are reusable. Selectors can also compute derived properties.
You can write selectors, such as the ones you saw in redux-saga. For example:
const getUsersNumber = ({ users }) => users.length;
const getUsersIds = ({ users }) => users.map(({ id }) => id);
etc...
You can also use reselect, which is a simple “selector” library for Redux, that memoize selectors to make them more efficient.
Selectors are functions that take Redux state as an argument and return some data to pass to the component.
const getUserData = state => state.user.data;
Why should it be used?
One of the main reasons is to avoid duplicated data in Redux.
Your data object shape keeps varying as your application grows, so rather than making changes in all the related component.It is much recommended/easier to change the data at one place.
Selectors should be near reducers because they operate on the same state. It is easier for data to keep in sync.
Using reselect helps to memoize data meaning when the same input is passed to the function, returns the previous result rather than recalculating again.So, this enhances your application performance.
function mapStateToProps (state) {
return {
user: state.user,
}
}
initialState of reducer by user store
const initialState = {
isAdmin:false,
isAuth:false,
access:[1,2,5]
};
class AppComp extends React.Component{
render(){
const {user: { access:access}} = this.props;
const rand = Math.floor(Math.random()*4000)
return (<div>
{`APP ${rand} `}
<input type="button" defaultValue="change auth" onClick={this.onChangeUserAuth} />
<p>TOTAL STATUS COUNT IS {access.length}</p>
</div>)
}
}}
but you can use selector
var getUser = function(state) {
return state.user
}
const getAuthProp = createSelector(
getUser,
(user) => user.access
);
function mapStateToProps (state) {
return {
// user: state.user,
access: getAuthProp(state)
}
}
Main Problem is this component use all user: state.user and any changes in user (etc isAdmin ,isAuth, access) runs rerender this component which need only part of this store - access!!!
In Redux, whenever an action is called anywhere in the application,
all mounted & connected components call their mapStateToProps
function. This is why Reselect is awesome. It will just return the
memoized result if nothing has changed.
In the real world, you will most likely need the same certain part of
your state object in multiple components.
https://medium.com/#parkerdan/react-reselect-and-redux-b34017f8194c
The createSelector function provided by Reselect implements the most basic way to derive a selector from previous selectors. The simplest use case is to derive a selector from a single other selector. In this case, the parameters to createSelector are the input selector and a function transforming the result of that selector into the result of the new selector. For example
var getProducts = function(state) {
return state.products
}
import {getProducts} from '../app/selectors'
import {createSelector} from 'reselect'
export const getProductTitles = createSelector(
getProducts,
(products) => products.map((product) => product.get('title'))
)
This is equivalent to (ignoring memoization):
import {getProducts} from '../app/selectors'
export const getProductTitles = (state) => {
return getProducts(state).map((product) => product.get('title'))
}
The createSelector function can combine data from multiple selectors as well as from a single selector. We can pass any number of selectors to createSelector, and their results will be passed to the function passed as the final argument. For a (somewhat contrived) example:
const isInCheckout = createSelector(
getIsShippingPage,
getIsBillingPage,
getIsConfirmationPage,
(isShipping, isBilling, isConfirmation) =>
isShipping || isBilling || isConfirmation
)
is equivalent to
const isInCheckout = (state) => {
return (
getIsShippingPage(state) ||
getIsBilingPage(state) ||
getIsConfirmationPage(state)
)
}
common pattern when writing mapStateToProps functions with selectors is to return an object with each key storing the result of a particular selector. The createStructuredSelector helper function in Reselect lets us write this pattern with the minimum of boilerplate. For example, if we writ
const mapStateToProps = createStructuredSelector({
title: getProductTitle,
price: getProductPrice,
image: getProductImage
})
it is equivalent to
const mapStateToProps = (state) => {
return {
title: getProductTitle(state),
price: getProductPrice(state),
image: getProductImage(state)
}
}
https://docs.mobify.com/progressive-web/0.15.0/guides/reselect/

Resources