Adding multiple objects to the store in redux - javascript

I've written an API call that returns all of a user's elements in an array.
I've written part of a reducer (it doesn't fully work yet, hence the question marks below) that looks like this:
export default function(state = defaultState, action) {
switch (action.type) {
case 'receiveElement':
return {
...state,
elementsMap: {
...state.elementsMap,
[action.element.id]: action.element,
},
visibleElements: [...state.visibleElements, action.element.id],
};
case 'receiveAllElements':
return {
...state,
elementsMap: {
...state.elementsMap,
**???**
},
visibleElements: [...state.visibleElements, ...action.elements.map((element, index) =>
`id-${element.id}`
)],
};
default:
return state;
}
}
defaultState and elementsMap looks like the following:
const defaultState = {
elementsMap: {
'id-1': {id: 'id-1', shape: 'circle', title: 'Run for City Council'},
'id-2': {id: 'id-2', shape: 'circle', title: 'NYU Law School'},
'id-3': {id: 'id-3', shape: 'circle', title: 'Start Company'},
},
visibleElements: ['id-1', 'id-2', 'id-3'],
};
I'm struggling to add the additional N elements coming back from the API call to the elementsMap and would love some help here, I think I've gotten the adding to visibleElements piece down.
Thanks for taking a look

Try this:
case 'receiveAllElements':
var map = elements.reduce((res, i) => { res[i.id] = i; return res; }, {});
return {
...state,
elementsMap: {
...state.elementsMap,
...map
},
visibleElements: [...state.visibleElements, ...action.elements.map((element, index) =>
`id-${element.id}`
)],
};

export default function(state = defaultState, action) {
switch (action.type) {
case 'receiveElement':
return {
...state,
elementsMap: {
...state.elementsMap,
[action.element.id]: action.element,
},
visibleElements: [...state.visibleElements, action.element.id],
};
case 'receiveAllElements':
let _visibleElements = [...state.visibleElements, ...action.elements.map((element, index) => `id-${element.id}`)]
return {
...state,
elementsMap: {
...state.elementsMap,
**???**
},
visibleElements: _visibleElements,
};
default:
return state;
}
}
I wouldn't also do any logic on the output object, I would do it as I showed above. Otherwise, code looks messy. But that's just my opinion

Related

Why useDispatch runs, but the state does not change?

Used in the project connect, but now I want to switch to hooks
When you enter input, the action is dispatched:
onChange={(e) => dispatch(actions.setInputText(e.target.value))}
but when outputting the todo element is an empty string, although when using connect everything worked fine?
const inputText = useSelector(state => state.inputText.data)
↑ This is a string with input text ↑
My project - scintillating-plantation.surge.sh
Code:
import React from "react";
import actions from "../../../actions";
import { useSelector, useDispatch } from 'react-redux'
export default function AddTodoInputBtn(props) {
const inputText = useSelector(state => state.inputText.data);
const inputActiveClass = useSelector(state => state.inputActiveClass.data);
const dispatch = useDispatch();
const inputNode = React.createRef();
function changeInputLogo() {
if (inputActiveClass === "") {
dispatch(actions.setInputActiveClass("_active"));
inputNode.current.focus();
return;
}
dispatch(actions.setInputActiveClass(""));
dispatch(actions.setInputText(""));
}
return (
<form
onSubmit={(e) => {
window.console.log(inputText); <----------------here is I show const inputText = useSelector(state => state.inputText.data);
dispatch(actions.setInputActiveClass(""));
inputNode.current.blur();
props.onAddTodo(e);
}}
className={`todo_block-add_todo add_todo_block add_todo_block${inputActiveClass}`}
>
<div
className={`add_todo_block-icon${inputActiveClass}`}
onClick={changeInputLogo}
/>
<input
ref={inputNode}
autoComplete="off"
type="text"
placeholder="Add List"
className="add_todo_block-input"
id="input"
value={inputText}
onChange={(e) => dispatch(actions.setInputText(e.target.value))}
onClick={changeInputLogo}
></input>
<button
type="submit"
className={`add_todo_block-btn add_todo_block-btn${inputActiveClass}`}
>
<div>Add</div>
</button>
</form>
);
}
reducer:
const first = {
data: "",
};
const second = {
data: false,
};
const third = {
data: JSON.parse(localStorage.getItem("todos")) || [
{ label: "Wash Kitchen", isComplete: false },
{ label: "Go To Theater", isComplete: false },
],
};
const fourth = {
data: [],
};
const fifth = {
data: [],
};
const sixth = {
data: "All",
};
const seventh = {
data: "",
};
function inputText(state = first, action) {
switch (action.type) {
case "CHANGE_INPUT_TEXT_DATA":
return { ...state, data: action.payload };
default:
return state;
}
}
function isShowTips(state = second, action) {
switch (action.type) {
case "CHANGE_SHOW_TIPS_DATA":
return { ...state, data: action.payload };
default:
return state;
}
}
function todoList(state = third, action) {
switch (action.type) {
case "CHANGE_TODO_LIST_DATA":
return { ...state, data: action.payload };
default:
return state;
}
}
function doneTodos(state = fourth, action) {
switch (action.type) {
case "CHANGE_DONE_TODOS_DATA":
return { ...state, data: action.payload };
default:
return state;
}
}
function undoneTodos(state = fifth, action) {
switch (action.type) {
case "CHANGE_UNDONE_TODOS_DATA":
return { ...state, data: action.payload };
default:
return state;
}
}
function todosType(state = sixth, action) {
switch (action.type) {
case "CHANGE_TODOS_TYPE_DATA":
return { ...state, data: action.payload };
default:
return state;
}
}
function inputActiveClass(state = seventh, action) {
switch (action.type) {
case "CHANGE_INPUT_ACTIVE_CLASS_DATA":
return { ...state, data: action.payload };
default:
return state;
}
}
export default {
inputText,
isShowTips,
todoList,
doneTodos,
undoneTodos,
todosType,
inputActiveClass,
};

ReactJS properly updating state with redux

I started working with reactjs recently and I need to know how to properly update my state.
My actions.js:
export function updateCreateCampaignObject(data) {
return {
type: actions.UPDATE_CREATE_CAMPAIGN,
payload: data,
}
}
export function updateCampaignProducts(data) {
return {
type: actions.UPDATE_CAMPAIGN_PRODUCTS,
payload: data,
}
}
export function updateCampaignTarget(data) {
return {
type: actions.UPDATE_CAMPAIGN_TARGET,
payload: data,
}
My reducer:
const INITIAL_STATE = {
campaign_dates: {
dt_start: '',
dt_end: '',
},
campaign_target: {
target_number: '',
gender: '',
age_level: {
age_start: '',
age_end: '',
},
interest_area: [],
geolocation: {},
},
campaign_products: {
survey: {
name: '',
id_product: '',
quantity: '',
price: '',
}
}
}
export default function createCampaignReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case UPDATE_CREATE_CAMPAIGN:
return { ...state, state: action.payload }
case UPDATE_CAMPAIGN_PRODUCTS:
return { ...state, campaign_products: action.payload }
case UPDATE_CAMPAIGN_TARGET:
return { ...state, campaign_products: action.payload }
default:
return state
}
In this case, I only want to add +1 to quantity in my campaign_products object.
Do I need to create an action just for this?
How can I call this action in my component, something like this?
import { updateCampaignProducts as updateCampaignProductsAction }
from '~/store/modules/createCampaign/actions'
function addQuantity() {
dispatch(updateCampaignProductsAction({
survey: {
quantity: quantity + 1
}
}))
}
Have not tested, But you need to change something like this. (3 steps)
1) Change quantity to number
campaign_products: {
survey: {
name: '',
id_product: '',
quantity: 0,
price: '',
}
}
2) In Reducer, based on action update the state from current state. (in your case not depend on any action payload)
export default function createCampaignReducer(state = INITIAL_STATE, action) { switch (action.type) {
case UPDATE_CAMPAIGN_PRODUCTS:
const current_quantity = state.campaign_products.survey.quantity;
return { ...state, campaign_products: current_quantity + 1}
default:
return state }
3) dispatch action
function addQuantity() {
dispatch(updateCampaignProductsAction())
}
you can do it with or without hooks.
both ways, read the docs about connecting redux to react.
react team wrote npm library to connent them.

Destructuring Actions in Nested Redux Reducer

I'm dispatching an action that looks like this:
{ type: "TOGGLE_FARA", fara: true, id: "5d20d019cf42731c8f706db1" }
The "id" is merely used for identifying the correct user. The action is meant to modify the "enabled" property of my "fara" state. The fara section of my store looks like this:
{
fara: {
enabled: false, // This need to be flipped...
names: []
},
senators: {
enabled: false,
names: []
},
senateCandidates: {
enabled: false,
names: []
}
}
However, I'm not sure how to structure my reducer. I only want to change the "enabled" property. I've tried a few different ways:
export default (state = DEFAULT_STATE, action) => {
switch (action.type) {
case "INITIALIZE_SETTINGS":
return {
fara: action.fara,
senators: action.senators,
senateCandidates: action.senateCandidates,
emails: action.emails
}
case "TOGGLE_FARA":
return {
...state,
// fara['enabled']: action.fara <–– This won't compile...
// 'fara.enabled' : action.fara <–––This just gives me a key name with the string 'fara.enabled'
}
default:
return state;
}
I was going to do something like this, but this modifys the state directly which you aren't supposed to do (my redux tool extension says the states are the same, which is not ideal). Any thoughts?
export default (state = DEFAULT_STATE, action) => {
switch (action.type) {
case "INITIALIZE_SETTINGS":
return {
fara: action.fara,
senators: action.senators,
senateCandidates: action.senateCandidates,
emails: action.emails
}
case "TOGGLE_FARA":
state.fara.enabled = action.fara;
return {
...state
};
default:
return state;
}
This will work:
return {
...state,
fara: { ...state.fara, enabled: action.fara }
}
and apparently is even in the Redux documentation

Redux state is not getting initialized to initial state

I am new to the react-redux. Here, I have a reducer which is like,
const initialState = {
Low: [
{
id: 0,
technologyId: 0,
technology: '',
type: '',
count: '',
allowded: 6,
level: 'EASY'
}
],
Medium: [
{
id: 0,
technologyId: 0,
technology: '',
type: '',
count: '',
allowded: 7,
level: 'MEDIUM'
}
],
High: [
{
id: 0,
technologyId: 0,
technology: '',
type: '',
count: '',
allowded: 7,
level: 'TOUGH'
}
]
}
export default function QuizData(state = initialState, action) {
switch (action.type) {
case QUIZ_DATA:
return {
...state,
[action.data.type]: [...action.data.tobeData],
error: false,
}
case ADD_NEW:
return {
...state,
[action.data.addtype]: action.data.addData,
error: false,
}
case REMOVE_TECH:
return {
...state,
[action.data.removeType]: action.data.newArr,
error: false,
}
case RESET_QUIZ:
return {
...initialState,
error: false,
}
}
Now, Here on click of button I am calling an action that will reset the data to initial state.
this.props.resetQuiz();
which is
export function resetQuiz() {
return {
type: RESET_QUIZ
}
}
where I use it
let newData = { ...this.props.data }; while using it in the component to do some operation.
Now here what happens is after doing some actions the initial state data gets changes with some new values,..
But,on click of the button I want to set it like, initialState.
So, when I tried that time, that initialState is also getting the same values. So, It is not resetting.
I am using it in component like,
data: state.QuizData // In statetoprops.
let criterias = [{
type: 'Low',
noc: 6,
id: 1,
data: this.props.data["Low"]
}, {
type: 'Medium',
noc: 7,
id: 2,
data: this.props.data["Medium"]
},
{
type: 'High',
noc: 7,
id: 3,
data: this.props.data["High"]
}]
While using the action in component like,
createQuestionBankQuiz = () => {
this.props.resetQuiz();
history.push({
pathname: "/quiz-setup/" + `${this.props.jdId}`
});
};
export default connect(mapStateToProps, { fetchListOfQuiz, updateQuestionViewMode, enableJob, resetQuiz })(LandingScreen);
The way I update is
onChange(event, tobeupdated, id, type, noc, data) {
let newData = { ...this.props.data };
let errorState;
let isDuplicate;
let addedRow;
if (newData) {
let data = newData[type].map((object, index) => {
if (object.id === id) {
object[tobeupdated] = event.target.value;
const tobeData = newData[type];
this.props.updateLowLevel({ tobeData, type }).then(() => {
let criteria_filled = this.disableAddbutton({ ...this.props.data }, type);
addedRow = `new${type}RowAdded`;
this.setState({
[addedRow]: criteria_filled ? true : false
})
const tobechecked = newData[type].filter(item => item.id === id);
isDuplicate = this.checkPreviousSelected(newData, type, tobechecked[0].technology, tobechecked[0].type);
if (isDuplicate) {
toastr.error("Duplicate criteria. Please change it.");
object["technology"] = '';
object["type"] = '';
const tobeData = newData[type];
this.props.updateLowLevel({ tobeData, type });
}
});
}
});
errorState = `show${type}Error`;
if (tobeupdated !== "count") {
this.getSelectionQuestionsNumber(type, id, noc);
}
let validateData = this.validate(type, noc);
this.setState({
[errorState]: validateData
})
}
}
What is it that I am doing wrong ?
I think you wrong with action dispatch
export function resetQuiz() {
return dispatch => {
dispatch({
type: RESET_QUIZ
})
}
}
I think better if you try with this in reducer
export default function QuizData(state = initialState, action) {
switch (action.type) {
case RESET_QUIZ:
return Object.assign({}, initialState, {error: false})
default:
return state;
}
}
you just need to return the initial value in reducer, like this for example:
export default function QuizData(state = initialState, action) {
switch (action.type) {
case RESET_QUIZ:
return initialState
default:
return state;
}
}
You forgot to return the state in your reducer for the default action :
export default function QuizData(state = initialState, action) {
switch (action.type) {
case QUIZ_DATA:
return {
...state,
[action.data.type]: [...action.data.tobeData],
error: false,
}
case ADD_NEW:
return {
...state,
[action.data.addtype]: action.data.addData,
error: false,
}
case REMOVE_TECH:
return {
...state,
[action.data.removeType]: action.data.newArr,
error: false,
}
case RESET_QUIZ:
return {
...initialState,
error: false,
}
return state; // you forgot this
}
redux issues some kind of init action when the store is initialized, so you need to return the given state in your reducers for others actions than the ones you defined yourself.
May be you are not doing deep copy in other actions.
Can you add the code of reducer for other actions?
Edit:
Quick solution for your problems is to change your reducer like below:
export default function QuizData(state = JSON.parse(JSON.stringify(initialState)), action) {
....
}
Note: JSON.parse(JSON.stringify(initialState)) works for your case but not for all the cases, and not a good solution. You have to write deep copy logic for that.
Correct/Proper solution:
You have to modify your component logic to not modify store data directly and update it through other actions.

Redux combineReducers, with child properties at same level as child reducers

I would like to have this shape of data:
let shop = Map({
shopInput: '',
shopShouldHideResults: true,
place: null,
position: {},
shopAutocompleteResults: {
predictions: [{ description: '' }]
}
})
let product = Map({
brand: '',
name: '',
description: '',
image: null,
price: null,
id: null,
item: 'MEAL'
})
export const addPage: Map<*, *> = Map({
shop: shop,
product: product,
shopListDisplayed: false,
rerenderKey: false
})
So addPage has a product reducer and a shop reducer and its own reducer just for its own two properties "shopListDisplayed" and "rerenderKey"
My reducers:
export const addPage = (
state: Map<*, *> = applicationState.get('addPage'),
action: AddPageAction
): Map<*, *> => {
switch (action.type) {
case UPDATE_SHOP_LIST_DISPLAYED: {
return state.set('shopListDisplayed', (action.payload: boolean))
}
case UPDATE_RERENDER_KEY: {
return state.set('rerenderKey', (!state.get('rerenderKey'): boolean))
}
default:
return state
}
}
export const product = (
state: Map<*, *> = applicationState
.get('addPage').get('product'),
action: AddPageAction
): Map<*, *> => {
switch (action.type) {
case UPDATE_BRAND: {
return state.set('brand', (action.payload: string))
}
case UPDATE_NAME: {
return state.set('name', (action.payload: string))
}
case UPDATE_ITEM_TYPE: {
return state.set('item', (action.payload: string))
}
case UPDATE_DESCRIPTION: {
return state.set('description', (action.payload: string))
}
case UPDATE_PRODUCT_ID: {
return state.set('id', (action.payload: number))
}
case UPDATE_PRICE: {
return state.set('price', (action.payload: number))
}
case UPDATE_IMAGE: {
return state.set('image', (action.payload: Image | any))
}
case UPDATE_IMAGE_LINK: {
return state.set('imageLink', (action.payload: string))
}
default:
return state
}
}
export const shop = (
state: Map<*, *> = applicationState.get('addPage').get('shop'),
action: Object
): any => {
switch (action.type) {
case UPDATE_SHOP_INPUT: {
return state.set('shopInput', action.payload.shopInput)
}
case UPDATE_SHOP_PLACE: {
return state.set('place', action.payload.place)
}
case UPDATE_SHOP_SHOULD_HIDE_RESULTS: {
return state.set('shopShouldHideResults', action.payload)
}
case GET_SHOP_AUTOCOMPLETE_RESULTS_REJECTED: {
console.log(
'there was an issue getting your autocomplete results: ',
action.payload
)
return state
}
case GET_SHOP_AUTOCOMPLETE_RESULTS_FULFILLED: {
return state.set('shopAutocompleteResults', action.payload)
}
case GET_SHOP_PLACE_DETAILS_FULFILLED: {
return state.set('place', action.payload.result)
}
case GET_SHOP_PLACE_DETAILS_REJECTED: {
console.log('there was an issue getting place details: ', action.payload)
return state
}
default:
return state
}
}
How do I use combineReducers in this case?
Currently I have:
const rootReducer = combineReducers({
addPage: combineReducers({shop, product}),
alertModal,
categories,
distanceSlider,
editProduct,
map,
menu,
searchPage: combineReducers({location}),
searchPageFields: searchPageFields,
searchResults,
searchResultsPresenter
I don't think my combineReducers takes into account shopListDisplayed: false,rerenderKey: false. I have not explicitely added in addPage reducer into combineReducers which deals with those two root properties of the addPage.
Where do I put that into combineReducers?
I get this error:
Unexpected properties "shopListDisplayed", "rerenderKey" found in the
previous state received by the reducer. Expected to find one of the
known reducer property names instead: "shop", "product". Unexpected
properties will be ignored.

Categories

Resources