I am trying to do a setting screen with two separate action functions. one for lb\kg and another for weight and calories, however if I change one of the functions the other gets hidden? or removed from screen.
Where I should be getting 150 lb, I can get either 150, or lb which are both created from separate actions. What am I doing wrong?
Where the reducer props get displayed.
<Text style={globalStyles.defaultText}>
Current Weight:{" "}
<Text>
{personalWeight} <--- be like 150
{weightProp} <---- be like lb
</Text>
{"\n"}
</Text>
actions page:
export const DISTANCE_SETTINGS = "DISTANCE_SETTINGS";
export const WEIGHT_SETTINGS = "WEIGHT_SETTINGS";
export const ALLINPUT_SETTINGS = "ALLINPUT_SETTINGS";
// settings button lists
export const settingsAction = (buttonGroupName, actionId) => dispatch => {
switch (buttonGroupName) {
case "distance":
dispatch({
type: DISTANCE_SETTINGS,
payload: actionId
});
break;
case "weight":
dispatch({
type: WEIGHT_SETTINGS,
payload: actionId
});
break;
default:
alert("There was an error somewhere");
}
};
// settings input options weight/calories
export const settingsInputs = data => dispatch => {
dispatch({
type: ALLINPUT_SETTINGS,
payload: data
});
};
reducers page:
import {
DISTANCE_SETTINGS,
WEIGHT_SETTINGS,
ALLINPUT_SETTINGS
} from "../actions/settingsAction";
export const inititalState = {
profile: {
weight: 150,
caloriesBurned: 100,
distanceSettings: "",
weightSettings: ""
}
};
export default function(state = inititalState, action) {
switch (action.type) {
case DISTANCE_SETTINGS:
return {
...state,
profile: {
distanceSettings: action.payload
}
};
case WEIGHT_SETTINGS:
let conversion = `${action.payload === "Pounds" ? "lb" : "kg"}`;
return {
...state,
profile: {
weightSettings: conversion
}
};
case ALLINPUT_SETTINGS:
return {
...state,
profile: {
weight: action.payload.weight,
caloriesBurned: action.payload.calories
}
};
default:
return state;
}
}
Your reducers should be:
export default function(state = inititalState, action) {
switch (action.type) {
case DISTANCE_SETTINGS:
return {
...state,
profile: {
...state.profile, // You don't have it
distanceSettings: action.payload
}
};
case WEIGHT_SETTINGS:
let conversion = `${action.payload === "Pounds" ? "lb" : "kg"}`;
return {
...state,
profile: {
...state.profile, // You don't have it
weightSettings: conversion
}
};
case ALLINPUT_SETTINGS:
return {
...state,
profile: {
...state.profile, // You don't have it
weight: action.payload.weight,
caloriesBurned: action.payload.calories
}
};
default:
return state;
}
}
Related
In this problem what can I do so that the on clicking the button, both the function add to the cart and select the checkbox executes together? In the current scenario add to cart is working when the button is clicked but the checkbox isn't selected. I removed all the styles so that the actual code is readable
YourItems.js
import React from "react";
import { connect } from "react-redux";
import { addOn } from "./data";
import { addOnhandleChange,addOnSelector} from "./AddOnActions";
const YourItems = ({ addOnhandleChange, addOnSelector, selectedId }) => {
return (
<div>
{addOn.map(({ id, name, img, price, unit }) => {
return (
<div key={id}>
<div>
<img src={img} alt={name} />
<p>{name}</p>
<span>Rs. {price}</span>
<input
type="checkbox"
checked={id === selectedId}
onChange={() => addOnhandleChange(id)}
/>
</div>
<button onClick={() =>addOnSelector({id, name,img,price,unit, })}>
Add
</button>
</div>
)})}
</div>
);
};
const mapStateToProps = (state) => {
return {
selectedId: state.addOn.selectedId,
};
};
export default connect(mapStateToProps, { addOnSelector,addOnhandleChange})(YourItems);
AddOnAction.js
export const addOnhandleChange = (id) => (dispatch) => {
dispatch({
type: "SELECTED_ID",
payload: id,
});
};
export const addOnSelector = ({ id, name, img, price, unit }) => (dispatch) => {
dispatch({
type: "ADD_TO_CART",
payload: { id, name, img, price, unit },
});
};
reducer.js
const initialState = {
selectedId: "",
};
export default function SelectorReducer(
state = initialState,
action
) {
switch (action.type) {
case "SELECTED_ID":
return {
...state,
selectedId: action.payload,
};
default:
return state;
}
}
data.js
export const addOn = [
{
id: 12654,
img: "https://images.pexels.com/photos/1132047/pexels-photo-1132047.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
name: "Banana",
price: 10,
unit: 1,
},
{
id: 2256435,
img: "https://images.pexels.com/photos/1132047/pexels-photo-1132047.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
name: "Mango",
price: 20,
unit: 1,
},
{
id: 3429684,
img: "https://images.pexels.com/photos/1132047/pexels-photo-1132047.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
name: "Grape",
price: 30,
unit: 1,
},
];
Add a case for "ADD_TO_CART" action type and use the id packed in action.payload in the reducer.
export default function SelectorReducer(
state = initialState,
action
) {
switch (action.type) {
case "SELECTED_ID":
return {
...state,
selectedId: action.payload,
};
case "ADD_TO_CART":
return {
...state,
selectedId: action.payload.id, // <-- use the payload.id
};
default:
return state;
}
}
I have a e-commerce store when user click on add item to cart, it add to redux store.
I have a checkout page where it lists all added item, if user click on remove item, it should be removed from store. Each item in checkout page have key as uuid.
I am using uuid to keep unique id for items when added to cart.
Suppose I have 2 items in cart , if user click delete in out item , it dispatches uuid of the item to store,
const initialState = {
items: []
};
const cart = (state = initialState, action) => {
switch (action.type) {
...
case "DELETE": {
return {
items: [
state.items.filter((item) => {
item.uuid !== action.payload;
}),
],
};
...
};
export default cart;
But after this all my cart items gets deleted and get warning in console that all items should have unique id.
Each item in state consists of below kind objects,
[{uuid: "uniqueid_1", name:'first'},{uuid: "uniqueid_2", name:'first'}]
I dispatches an remove action consists of payload as uuid (ie,"uniqueid_1")
The complete code reference below,
Action
import { v4 as uuidv4 } from "uuid";
const add = (item, quantity) => {
return {
type: "ADDTOCART",
payload: {
uuid: uuidv4(),
id: item.id,
image: item.photo,
name: item.name,
price: item.price,
quantity: quantity,
},
};
};
const remove = (item) => {
return {
type: "DELETE",
payload: item,
};
};
const removeall = () => {
return {
type: "REMOVEALL",
};
};
export default { add, remove, removeall };
REDUCER
const initialState = {
items: [],
};
const cart = (state = initialState, action) => {
switch (action.type) {
case "ADDTOCART": {
return { items: [...state.items, action.payload] };
}
case "DELETE": {
return {
items: [
state.items.filter((item) => {
item.uuid !== action.payload;
}),
],
};
}
case "REMOVEALL": {
return { items: [] };
}
default:
return state;
}
};
export default cart;
REMOVE ACTION
onClick={() => {dispatch(cartAction.remove(product.uuid));}}
Your mistake could be that you now have an array too much. You can change it to:
const initialState = {
items: []
};
const cart = (state = initialState, action) => {
switch (action.type) {
...
case "DELETE": {
return {
items:
state.items.filter((item) => {
item.uuid !== action.payload.uuid;
}),
};
...
};
export default cart;
This is because state.items.filter will already return an array of objects, so you don't need to wrap it in []
Also Due to the fact that you are using the item as payload for the delete action you need to use item.uuid !== action.payload.uuid for comparison
Add the Spread Syntax(...) here:
items: [
...state.items.filter((item) => {
item.uuid !== action.payload;
})
],
because this:
[{uuid: "uniqueid_1", name:'first'},{uuid: "uniqueid_2", name:'first'}].filter(x=>x.uuid!=="uniqueid_1")
return an array.
your items array it inside another array at the end, like this:
[[{uuid: "uniqueid_1", name:'first'}]]
And you need this:
[{uuid: "uniqueid_1", name:'first'}]
I have removed curly braces from filtering and it worked
items: [
state.items.filter((item) =>
item.uuid !== action.payload;
),
],
So I basically started learning Redux and wanted to create a simple store app where you can add the phones to the cart. I have created a state object and within it, I have created an array with objects with a list of items in the store. I wanted to update on [+] click the number of items ordered but it doesn't work for now. I have been struggling with that for 1 hour already and still do not see where the problem might be.
Reducer looks like that:
const initialState = {
liked:0,
cart:0,
item: [
{
id:1,
name: 'Iphone 8',
price: 2000,
desc: 'The new Iphone 8 available at our store!',
orderedNum: 0
},
{
id:2,
name: 'Iphone 6',
price: 1500,
desc: 'The new Iphone 6 available at our store!',
orderedNum: 0
},
{
id:3,
name: 'Samsung S8',
price: 2200,
desc: 'The new Samsung S8 available at our store!',
orderedNum: 0
},
{
id:4,
name: 'Xiaomi Mi 6',
price: 1400,
desc: 'The new Xiaomi Mi 6 available at our store!',
orderedNum: 0
},
{
id:5,
name: 'Pocophone P1',
price: 2100,
desc: 'The new Pocophone P1 available at our store!',
orderedNum: 0
},
{
id:6,
name: 'Nokia 3310',
price: 999,
desc: 'The new Nokia 3310 available at our store!',
orderedNum: 0
},
]
}
const reducer = (state = initialState, action) => {
const newState = {...state};
switch(action.type) {
case 'ADD_NUM':
return state.item.map((el, index) => {
if(el.id === action.id ){
return {
...el,
orderedNum: el.orderedNum + 1
}
}
return el;
})
default:
break;
}
return newState;
}
export default reducer;
I have the action:
const mapStateToProps = state => {
return {
item: state.item
}
}
const mapDispatchToProps = dispatch => {
return {
addNum: () => dispatch ({
type: 'ADD_NUM',
id: this.props.id,
value: 1
})
}
I have tried it in a different ways but I believe it could be the problem with nesting in the reducer.
Could someone advise?
Lets start with your reducer
const reducer = (state = initialState, action) => {
switch (action.type) {
case "ADD_NUM":
return {
// destruct and return a new object otherwise react wont update the UI
...state,
item: state.item.map(el =>
el.id === action.id
? { ...el , orderedNum: el.orderedNum + action.value }
: el
)
};
default:
return state;
}
};
mapDispatchToProps
const mapDispatchToProps = dispatch => {
return {
// add id to addNum
addNum: id =>
dispatch({
type: "ADD_NUM",
id,
value: 1
})
};
};
Items component
const Items = ({ item, addNum }) => (
item.map(el => (
<div key={el.id}>
<h1>{el.name}</h1>
<h3>{el.price}</h3>
<h3>{`orderedNum: ${el.orderedNum}`}</h3>
// add the id to addNum
<button onClick={() => addNum(el.id)}>+</button>
</div>
))
);
CodeSandBox
Your reducer should look something like this:
export default (state = initialState, action) => {
switch (action.type) {
case 'ADD_ITEM':
return [...state, action.item];
case 'REMOVE_ITEM':
return state.filter(({ id }) => id !== action.id);
default:
return state;
}
};
As pointed out in the comments the above will work for an initial state that is an array but not an object... Here is how to handle it if it's an object (with two helper methods from lodash mapKeys and omit:
export default (state = {}, action) => {
switch (action.type) {
case FETCH_ITEMS:
return { ...state, ...mapKeys(action.payload, 'id') };
case FETCH_ITEM:
return { ...state, [action.payload.id]: action.payload };
case CREATE_ITEM:
return { ...state, [action.payload.id]: action.payload };
case EDIT_ITEM:
return { ...state, [action.payload.id]: action.payload };
case DELETE_ITEM:
return omit(state, action.payload);
default:
return state;
}
};
I have normalized by data in redux, but now grabbing and display it on the front end is causing issues.
Ive been trying to fix this for a few days, i've tried what i feel is the most obvious this.props.... but i'm clearly missing something. Was using Manning newest book on Redux, Redux in Action, which has been a great guide for me so far.
I was trying to break up the data, so the tree would have business (dashboard) and business_locations (another page).
in my Redux DevTools console, the Action Tree looks like:
type: 'RECEIVE_ENTITIES'
payload
entities
business_locations
- 652 {id: 652, business_id: 452...}
business
- 452 {id: 452}
result: 452
my businessReducer:
import {
RECEIVE_ENTITIES,
FETCH_BUSINESS_STARTED,
FETCH_BUSINESS_SUCCESS,
SET_CURRENT_BUSINESS_ID,
} from '../../actions/types';
const initialBusinessState = {
items: [],
isLoading: false,
error: null,
};
export const getBusinesses = state => {
return Object.keys(state.business.items).map(id => {
return state.business.items[id];
});
};
export default function businesses(state = initialBusinessState, action) {
switch (action.type) {
case RECEIVE_ENTITIES: {
const { entities } = action.payload;
if (entities && entities.businesses) {
return {
...state,
isLoading: false,
items: entities.businesses,
};
}
return state;
}
case FETCH_BUSINESS_STARTED: {
return {
...state,
isLoading: true,
};
}
case FETCH_BUSINESS_SUCCESS: {
return {
...state,
isLoading: true,
items: action.payload.businesses,
};
}
default: {
return state;
}
}
}
export function setCurrentBusinessId(id) {
return {
type: SET_CURRENT_BUSINESS_ID,
payload: {
id,
},
};
}
my businessActions;
import { normalize } from 'normalizr';
import {
FETCH_BUSINESS_STARTED,
FETCH_BUSINESS_ERROR,
RECEIVE_ENTITIES,
SET_CURRENT_BUSINESS_ID,
} from './../types';
import * as schema from '../schema';
import * as api from '../../api';
function fetchBusinessStarted() {
return {
type: FETCH_BUSINESS_STARTED,
};
}
function fetchBusinessFailed(err) {
return {
type: FETCH_BUSINESS_ERROR,
payload: {
err,
},
};
}
function receiveEntities(entities) {
return {
type: RECEIVE_ENTITIES,
payload: entities,
};
}
export function setCurrentBusinessId(id) {
return {
type: SET_CURRENT_BUSINESS_ID,
payload: {
id,
},
};
}
export function fetchBusinesses() {
return (dispatch, getState) => {
dispatch(fetchBusinessStarted());
return api
.fetchBusinesses()
.then(resp => {
const businesses = resp.data;
const normalizedData = normalize(businesses, schema.businessSchema);
console.log('normalizedData', normalizedData);
dispatch(receiveEntities(normalizedData));
if (!getState().page.currentBusinessId) {
const defaultBusinessId = sessionStorage.getItem('business_id');
dispatch(setCurrentBusinessId(defaultBusinessId));
}
})
.catch(err => {
console.log(err);
fetchBusinessFailed(err);
});
};
}
my pageReducer
import { SET_CURRENT_BUSINESS_ID } from '../../actions/types';
const InitialPageState = {
currentBusinessId: null,
};
export default function page(state = InitialPageState, action) {
switch (action.type) {
case SET_CURRENT_BUSINESS_ID: {
return {
...state,
currentBusinessId: action.payload.id,
};
}
default: {
return state;
}
}
}
my schema
import { schema } from 'normalizr';
const businessLocationSchema = new schema.Entity('business_locations');
const businessSchema = new schema.Entity('business', {
business_locations: [businessLocationSchema],
});
export { businessSchema };
rootReducer
const rootReducer = (state = {}, action) => {
return {
page: pageReducer(state.page, action),
business: businessReducer(state.businesses, action),
businessLocation: businessLocationsReducer(state.businessLocations,
action),
form: formReducer,
};
};
front end:
class Dashboard extends React.Component {
componentDidMount() {
this.props.dispatch(fetchBusinesses());
}
render() {
if (this.props.isLoading) {
return (
<SpinContainer>
<Spin size="large" />
</SpinContainer>
);
}
console.log(this.props);
return (
<Wrapper>
<Card bordered={false}>
<Maps
zoom={16}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `30vh` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
<p>{this.props.business.website}</p>
<p>{this.props.business.businessName}</p>
</Card>
<Link to="/locations">
<Card bordered={false}>
<h1>****</h1>
</Card>
</Link>
<Link to="stamp-cards">
<Card bordered={false}>
<h1>*****</h1>
</Card>
</Link>
<Card bordered={false}>
<h1>*****</h1>
</Card>
</Wrapper>
);
}
}
function mapStateToProps(state) {
const { isLoading, business } = state.business;
return {
business: getBusinesses(state),
isLoading,
};
}
export default connect(mapStateToProps)(Dashboard);
How do I target which element/object in array in a reducer I have to pass the action to? I am trying to figure out what action I take on the reducer. Or how do I change affect the number value in the object. So I am rendering the array in a View with TouchableOpacity which onPress Im calling the action dispatch as following:
import React, { Component } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
class NewData extends Component {
render(){
const renData = this.props.newData.map((data, idx) => {
return (
<View key={idx}>
<TouchableOpacity
onPress={() => this.props.incNum(data.id)}
//not sure how to toggel dispatch call above, for onPress
>
<Text style={styles.welcome}>{data.id} - {data.name} - {data.number}</Text>
</TouchableOpacity>
</View>
)
});
return(
<View>
{renData}
</View>
);
}
}
function mapStateToProps (state) {
return {
newData: state.newData
};
};
//I think I need to create a toggle here:
//Something like: this.props.newData.id ? incNum : decNum
function mapDispatchToProps (dispatch) {
return {
incNum: (id) => {
dispatch({
type: "INC_NUM",
payload: id
})
},
decNum: (id) => {
dispatch({
type: "DEC_NUM",
payload: id
})
}
};
};
export default connect( mapStateToProps, mapDispatchToProps )( NewData )
My Reducer:
const initialState = [
{
id: '01',
name: 'Name One',
number: 11
},
{
id: '02',
name: 'Name Two',
number: 22
},
{
id: '03',
name: 'Name Three',
number: 33
}
]
export default function newData (state = initialState, action) {
switch (action.type) {
case "INC_NUM":
return {
...state,
number: this.state.number ++ // <== need help here.
}
case "DEC_NUM":
return {
...state,
number: this.state.number --
}
default:
return state
}
}
I think <TouchableOpacity onPress={() => this.props.incNum(data.id)} will bind the id to be changed. And passing the payload: id will pass the id to the reducer. But how do I update the value in the reducer? I can do without the toggle for now. I want to learn about passing the value and updating accordingly in the reduder.
Thank you.
EDIT1 :
So my reducer now is:
const initialState = {
1: {
id: '01',
name: 'Name One',
number: 11
},
2: {
id: '02',
name: 'Name Two',
number: 22
}
}
export default function newData (state = initialState, action) {
switch (action.type) {
case "INC_NUM":
return {
...state,
[action.payload]: {
...state[action.payload],
number: state[action.payload].number++ <== Line 58 in ERROR screenshot
}
case "DEC_NUM":
return {
...state,
[action.payload]: {
...state[action.payload],
number: state[action.payload].number--
}
default:
return state
}
}
And the way I'm rendering it is:
class NewData extends Component {
render(){
const renData = Object.keys(this.props.newData).map((key, idx) => {
let data = this.props.newData[key]
return (
<View key={idx}>
<TouchableOpacity
onPress={() => this.props.updateNum(data.id)}
>
<Text style={styles.welcome}>{data.id} - {data.name} - {data.number}</Text>
</TouchableOpacity>
</View>
)
});
return(
<View>
{renData}
</View>
)
}
}
function mapDispatchToProps (dispatch) {
return {
updateNum: (id) => {
INC_NUM({
type: "INC_NUM",
payload: id
})
}
};
};
Everything in the reducer is appearing as expected. But when I click and the action is called, I get an error:
your reducer seems to be lot more complex that it needs to be, below is the simpler approach, hope this helps.
const initialState = {
1: {
id: '01',
name: 'Name One',
number: 11
},
2: {
id: '02',
name: 'Name Two',
number: 22
}
}
export default function newData (state = initialState, action) {
switch (action.type) {
case "INC_NUM":
const newState = {...state};
newState[action.payload].nubmer++;
return newState;
case "DEC_NUM":
const newState = {...state};
newState[action.payload].nubmer--;
return newState;
default:
return state
}
}
Your problem appears to be in your reducer and the structure of your state.. you're not using the ID to identify which keyval pair to update.
First I'd modify your initialState to be an object and use the id as your keys:
const initialState = {
1: {name: 'Name One', number: 11},
2: {name: 'Name Two', number: 22},
3: {name: 'Name Three', number: 33}
}
Then in your reducer:
case "INC_NUM":
return {
...state,
[action.id]: {
...state[action.id],
number: state[action.id].number++
}
}
I know the syntax is convoluted, but the spread operator is nice shorthand for what otherwise would be even harder to read.