this is my code can un help with it how to write slice function properly
export default function(state={inputs:[ ]}, action) {
console.log(action.index);
switch (action.type) {
case APPEND_INPUT:
return { ...state, inputs: state.inputs.concat({value:"",key:"1",type:'input'})};
case APPEND_TEXTAREA:
return { ...state, inputs: state.inputs.concat({value:"",key:"",type:'textarea'})};
case APPEND_EMAIL:
return { ...state, inputs: state.inputs.concat({value:"",key:"",type:'email'})};
case REMOVE_INPUT:
return {inputs: state.inputs.slice(0)};
default:
return state;
}
}
Or:
{inputs: state.inputs.filter((_, i) => i !== action.index))
Slice the array up to the index, and concat it to the slice from the index + 1 onwards:
{
inputs: state.inputs.slice(0, action.index).concat(state.inputs.slice(action.index + 1))
}
If you use ES6 you can do this instead of concat:
{
inputs: [...state.inputs.slice(0, action.index), ...state.inputs.slice(action.index + 1)]
}
Related
I have the first asynchronous function
fetch("https://api.priceapi.com/v2/jobs", {
body: body,
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
method: "POST"
}).then((response) => {
return response.json();
}).then((data) => {
return fetchRepeat(data.job_id)
})
And the second recursive asynchronous function.
function fetchRepeat(id){
fetch("https://api.priceapi.com/v2/jobs/"+ id +"/download.json?token=" + priceapisecret.secret)
.then((response) => {
return response.json()
}).then((data) =>{
if(data.status == "finished"){
var bookdata = {
title: data.results[0].content.name,
price: data.results[0].content.price
}
return bookdata;
}
else{
fetchRepeat(id)
}
})
}
I want to be able to access bookdata in the first async function. How do I do that?
In order to talk about a return your fetchRepeat needs to return the promise. It did not so returning undefined was the result. The last then also didn't return the value of the recursion and thus also resolved to undefined.
Here is a working version:
function fetchRepeat(id) {
// return the promise
return fetch(`https://api.priceapi.com/v2/jobs/${id}/download.json?token=${priceapisecret.secret}`)
.then(response => response.json())
.then(({ status, results: [{ content: { name: title, price } }] = [{ content: {} }] }) =>
(status === 'finished' ? { title, price } : fetchRepeat(id))); // return result of recursion
}
Now I let ESLint handle the formatting and since I use airbnb it prefers destructuring. The error in the last then was obvious since ELSint complained about consistent return. I urge you to use a linter and an IDE which enforces a coding style to reduce bugs in your code and make it easier for others to read.
import types from "../actions/types";
export default function(state = null, action) {
switch (action.type) {
case types.fetchCartProducts:
return action.payload || false;
case types.modifyCart:
debugger;
switch (action.payload.operation) {
case "subtract":
const index = action.payload.index;
let isSingleCount = state[index] === 1;
let chosenIds = state;
if (isSingleCount) {
chosenIds = chosenIds.filter(index => index != index);
} else {
[
...chosenIds.slice(0, index),
{ ...chosenIds[index], count: chosenIds[index].count - 1 },
...chosenIds.slice(index + 1)
];
}
return (
chosenIds
)
}
default:
return state;
}
}
{
"Products": [
{
index: 1,
name: "Shirt",
price: 1.9,
count: 2
},
{
index: 2,
name: "Jeans",
price: 1.9,
count: 2
}
]
}
I have a react component showing cart products. Each product in the cart is a seperate div and having + and - buttons to increase, decrease the no of that product. On - click I want to decrease the quantity and also if count is reduced to 0 I want to remove this product as well from my redux state.
Now I have my reducer where first I am checking if the count is 1 then removing the product itself else reducing the count only. I am returning the state but its not updating the DOM
Can anyone help in this am I doing something wrong in returning state.
Thanks
It looks like you are directly manipulating the state, which will cause problems in React. Instead of let chosenIds = state;, you should copy the state, let chosenIds = Object.assign({}, state);. Then you can manipulate chosenIds as you wish.
Looks like you forgot to include a assignment statement in the else block.
} else {
chosenIds = [
...chosenIds.slice(0, index),
{ ...chosenIds[index], count: chosenIds[index].count - 1 },
...chosenIds.slice(index + 1)
];
}
Instead of this complicated operation, you could use array.map to update a single item in the array.
chosenIds = state.map(item => item.index === index ? {...item, count: item.count - 1} : item)
I've just normalised the state of an app I'm working on (based on this article) and I'm stuck trying to add/remove items from part of my state tree based on quantity.
Part of my state tree cart is solely responsible for housing the quantity of tickets that are in the cart, organised by ID. When the user changes the quantity, an action is dispatched UPDATE_QTY which has the qty and the id.
The state starts off correct as the incoming data has the qty but I can't seem to figure out the syntax to remove the item from the cart reducer if qty is 0, also how to add it back in if the qty is 1 or more.
Could someone offer advice on the correct syntax to achieve this please?
EDIT: I'm wondering if I'm trying to do too much inside the UPDATE_QTY action and that I should have separate actions for deleting and adding items.
byId reducer
export function byId(state = initialState, action) {
switch (action.type) {
case SET_INITIAL_CART_DATA:
return Object.assign({}, state, action.tickets);
case UPDATE_QTY: // Here, I need to check if action.qty is 0 and if it is I need to remove the item but also add it back in if action.qty > 0
return {
...state,
[action.id]: { ...state[action.id], qty: action.qty }, // Updating the qty here works fine
};
default:
return state;
}
}
Simplfied state tree
const state = {
cart: {
byId: {
'40': { // How can I remove these items when qty is 0 or add back in if > 0?
qty: 0,
id: '40'
},
'90': {
qty: 0,
id: '90'
}
},
allIds: [
[
'40',
'90',
]
]
},
}
I also need the IDs to be reflected in my allIds reducer.
allIds reducer
export function allIds(state = [], action) {
switch (action.type) {
case SET_INITIAL_CART_DATA:
return [...state, ...action.allIds];
case UPDATE_QTY:
return [ONLY IDS WITH QTY]
default:
return state;
}
}
For this I'm not sure if the allIds reducer needs to be connected to the byIds reducer and take information from there. I would love to hear what best practice for something like this would be.
Why have separate reducers for byIds and allIds? I would combine these into one cart reducer and maintain the allIds state with byIds:
case SET_INITIAL_CART_DATA:
// just guessing here...
const { tickets } = action;
const allIds = tickets
.reduce((arr, ticket) => arr.concat(ticket.id), []);
return {
byIds: { ...tickets },
allIds
}
case UPDATE_QTY: {
const { byIds, allIds } = state;
const { id, qty } = action;
const idx = allIds.indexOf(id);
const next = { };
if (qty > 0) {
next.byIds = {
...byIds,
[id]: { id, qty }
};
next.allIds = idx === -1 ? allIds.concat(id) : [ ...allIds ];
return next;
}
next.byIds = { ...byIds };
delete next.byIds[id];
next.allIds = idx === -1 ? [ ...allIds ] : [
...allIds.slice(0, idx),
...allIds.slice(idx + 1)
];
return next;
}
However, what state do you want normalized? If this represents a shopping cart of tickets, the tickets are what would be normalized, and the cart would just represent the quantity of tickets to be purchased. Then your state would look something like this:
{
tickets: {
byIds: {
'1': { id, name, price, ... },
'2': { ... },
'3': { ... },
...
}
allIds: [ '1', '2', '3', ... ]
},
cart: [
{ id: 2, qty: 2 },
{ id: 1, qty: 1 }
]
}
The use of an array for the cart state maintains insertion order.
Sometimes (when you only iterate through ids and get by id) it's enough to remove id from allIds and skip all unnecessary computations.
case actionTypes.DELETE_ITEM: {
const filteredIds = state.allIds.filter(id => id !== action.itemId);
return {
...state,
allIds: filteredIds
};
}
So i have a redux store that looks like this:
posts : {
posts: [
0: { },
1: { }
]
}
I want to concat the array with extra items like this:
case LOAD_MORE_POSTS_SUCCESS:
return {
...state,
posts: [...state.posts, action.posts],
isFetching: false,
}
However this is pushing the extra posts only into the first level, ive tried doing something like this (no luck):
case LOAD_MORE_POSTS_SUCCESS:
return {
...state,
posts: [...state.posts.posts, action.posts],
isFetching: false,
}
Have you tried something like:
case LOAD_MORE_POSTS_SUCCESS:
return {
...state,
posts: {
posts: [...state.posts.posts, action.posts]
},
isFetching: false,
}
And if action.posts is an array (the plural form hints that it is), consider using:
posts: {
posts: [...state.posts.posts, ...action.posts]
}
Thanks to #Gila Artzi - My final code looked like this:
case LOAD_MORE_POSTS_SUCCESS:
return {
...state,
posts: [...state.posts, ...action.posts],
isFetching: false,
}
I'm trying to figure out why getLabelForType works but getOtherLableForType fails typing:
export const things = {
THING1: 'THING1',
THING2: 'THING2',
}
export type Thing = $Keys<typeof things>
type Empty = 'empty type' & 'nothing there'
export default function unexpectedCase(impossible: Empty): void {
throw new Error(`Unexpected case ${impossible}`)
}
export function getLabelForType(thing: Thing): string {
switch (thing) {
case 'THING1':
return 'Thing 1'
case 'THING2':
return 'Thing 2'
default:
unexpectedCase(thing)
return ''
}
}
export function getOtherLabelForType(thing: Thing): string {
(things.THING1: Thing)
switch (thing) {
case things.THING1:
return 'Thing 1'
case things.THING1:
return 'Thing 2'
default:
unexpectedCase(thing)
return ''
}
}
See the tryflow working example