State Not Changing Properly In React Redux - reactjs

I am new to React Redux I have initialized state which is properly displaying by calling its respective reducer which job to return the initialize State. But after mutating the state the state most probably not changing and i cant figure it out why
import { combineReducers } from 'redux';
const initialState = {
songs:[
{title:'No Scrubs', duration: '4:05'},
]
}
const songReducers = (state = initialState)=>{
return state
}
const selectedSongReducer =(selectedSong=null,action)=>{
if(action.type==='SONG_SELECTED'){
return action.payload
}
return selectedSong;
}
const addSongReducer = (state=initialState,action)=>{
if(action.type==="ADD_SONG"){
return {
...state,
songs:[
...state.songs, {title:'All demo', duration: '4:05'}
]
}
}
return state;
}
export default combineReducers({
songs: songReducers,
selectedSong: selectedSongReducer,
addSong:addSongReducer
})

Your overall state shape will look like
state = {
songs: {
songs: [
{ title: 'No Scrubs', duration: '4:05' },
]
},
selectedSong: null,
addSong: {
songs: [
{ title: 'No Scrubs', duration: '4:05' },
]
}
}
after the very first reducer call. Is this what you want? Asking because you have
state = {
songs: {
songs: [
{ title: 'No Scrubs', duration: '4:05' },
]
},
...
}
instead of just
state = {
songs: [
{ title: 'No Scrubs', duration: '4:05' },
]
...
}

Related

How to update nested state in redux

I'm a bit stuck with redux. I want to create reducer that can update state onClick with data that provided in each button.
Here's my TabSlice.ts
interface ITabContext {
tabIndex: number,
posterUrlFetch: string,
tabData: {
fetchUrl: string;
title: string;
}[]
}
const initialState = {
tabIndex: 0,
posterUrlFetch: 'movie/popular',
tabData: [
{ fetchUrl: 'movie/popular', title: 'Trending' },
{ fetchUrl: 'movie/upcoming', title: 'Upcoming' },
{ fetchUrl: 'tv/popular', title: 'TV Series' },
]
}
const tabSlice = createSlice({
name: 'tab',
initialState: initialState as ITabContext,
reducers: {
changeTab(state, action: PayloadAction<ITab>) {
const newItem = action.payload;
return state = {
tabIndex: newItem.tabIndex,
posterUrlFetch: newItem.posterUrlFetch,
tabData: [
{ fetchUrl: newItem.posterUrlFetch, title: newItem.posterUrlFetch },
]
}
}
}
})
Then I dispatch changeTab in my component and create function onClick:
const click = () => dispatch(changeTab({
tabIndex: 1,
posterUrlFetch: 'movie/popular',
tabData: [
{
fetchUrl: 'tv/latest',
title: 'TV Latest'
},
{
fetchUrl: 'movie/popular',
title: 'Popular'
},
{
fetchUrl: 'movie/latest',
title: 'Latest'
},
]
}));
As i click some info updates, but in tabData I have only first object. How to make it to push all data to tabData, not only first one? Thanks!
Remove return state = {} from your reducer function and instead return the object as a whole.
return {
tabIndex: newItem.tabIndex,
posterUrlFetch: newItem.posterUrlFetch,
tabData: newItem.tabData,
};
For the payload's tabData you can pass newItem.tabData

How to modify existing data in reducer?

I'm new to react-redux, I was working on a tutorial and I wanted to add a few features of my own.
How do I add a new method to add a new song to the existing array of song objects > I was confused because theres already a song key in combine reducers. What should I return/pass as a parameter to add a new song?
import { combineReducers } from "redux";
const songsReducer = () => {
return [
{
title: "song one",
duration: "4:30"
},
{
title: "song one",
duration: "4:00"
},
{
title: "song one",
duration: "3:28"
},
{
title: "song one",
duration: "3:50"
}
];
};
const selectedSongReducer = (selectedSong = null, action) => {
if (action.type === "SONG_SELECTED") {
return action.payload;
}
return selectedSong;
};
**const addSong = () => {};** // need help with this function
export default combineReducers({
songs: songsReducer,
selectedSong: selectedSongReducer
});
const addSong = song => ({
type: 'ADD_SONG',
payload: song,
});
const songsReducer = (songs = [], action) => {
switch (action.type) {
case 'ADD_SONG': {
return [...songs, action.payload.song]; //immutable
}
default: {
return [
{
title: 'song one',
duration: '4:30',
},
{
title: 'song one',
duration: '4:00',
},
{
title: 'song one',
duration: '3:28',
},
{
title: 'song one',
duration: '3:50',
},
];
}
}
};
The action will return a song with type ADD_SONG:
const addSong = (song) => {
return {
type: "ADD_SONG",
payload: {
song
}
}
}
In reducer:
const selectedSongReducer = (selectedSong = [], { type, payload = {} }) => {
if (type === "SONG_SELECTED") {
return payload;
}
if (type === "ADD_SONG") {
return [...selectedSong].push(payload.song) // Imutable selectedSong
}
return selectedSong;
};
Always use switch case for cleaner code
and return like this return [...selectedSong, action.payload];
to avoid mutation
const addSong = (song) => {
return {
type: "ADD_SONG",
payload: {
song
}
}
}
const selectedSongReducer = (selectedSong = [], action) => {
switch (action.type) {
case 'SONG_SELECTED': {
return [...selectedSong, action.payload.song];
}
default: null
}
};
const songsReducer = (song = [], action) => {
switch (action.type) {
case 'ADD_SONG': {
return [...songs, action.payload.song];
}
default: {
return [
{
title: 'song one',
duration: '4:30',
},
{
title: 'song one',
duration: '4:00',
},
{
title: 'song one',
duration: '3:28',
},
{
title: 'song one',
duration: '3:50',
},
];
}
}
};

Use destructuring assignment inside state

class BottomPanelProgramTabs extends React.Component<Props, State> {
state = {
activeTab: this.props.children[0].props.label,
};
...
ESLint want me to use destructuring assignment on
this.props.children[0].props.label
any ideas on how to do that?
you can do like this.
For more reference about prefer-destructuring
class BottomPanelProgramTabs extends React.Component<Props, State> {
constructor(){
let [props] = this.props.children;
state = {
activeTab : props.label
}
}
class BottomPanelProgramTabs extends React.Component<Props, State> {
state = {
activeTab: 'default label'
};
componentDidMount = () => {
const [{ props: { label } }] = this.props.children;
this.setState({
activeTab: label || 'default label',
...
})
}
...
You can mix destructing by getting the first element with [] and get the props with {}.
For example:
using [child] will give us the first element in the array.
const children = [{
props: {
label: 'Some label'
}
},
{
props: {
label: 'Second label'
}
},
,
{
props: {
label: 'another label'
}
}]
const [child] = children;
console.log(child);
to get props we can continue mixing our destruction by adding [ {props} ] which returns props object.
const children = [{
props: {
label: 'Some label'
}
},
{
props: {
label: 'Second label'
}
},
,
{
props: {
label: 'another label'
}
}]
const [ {props} ] = children;
console.log(props);
to get the label from props will can do this [{ props: { label } }]
const children = [{
props: {
label: 'Some label'
}
},
{
props: {
label: 'Second label'
}
},
,
{
props: {
label: 'another label'
}
}]
const [{ props: { label } }] = children;
console.log(label);
With complex data
const children = [{
props: {
label: [{
data: {
title: 'complex destructor'
}
},
{
data: {
title: 'complex destructor 2'
}
}]
}
},
{
props: {
label: 'Second label'
}
},
,
{
props: {
label: 'another label'
}
}]
const [{ props: { label: [ { data: { title } } ] } }] = children;
console.log(title)

How to update nested objects state of array in redux?

I want to update state of an object in array of array with particular index from the action payload. I have this kind of state in reducers.
const INITIAL_STATE = {
tasks: {
taskArray: [
{
maintask: 'Main task 1',
subtask: [
{
photo_urls: [],
task_name: 'Task 1',
task_status: false
},
{
photo_urls: [],
task_name: 'Task 2',
task_status: false
}
]
},
{
maintask: 'Main task 2',
subtask: [
{
photo_urls: [],
task_name: 'Task 1',
task_status: false
},
{
photo_urls: [],
task_name: 'Task 2',
task_status: false
}
]
}
]
}
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case ON_CHECK_BOX_CLICK:
return state;
default:
return state;
}
};
I have an object of subtask in action.payload. First I want to check index of an object in array and then I want to update state of particuler array of object.
I'll suggest to use https://github.com/kolodny/immutability-helper As a payload you may send a result of the update function:
import update from 'immutability-helper';
update(currentState, {
tasks: {
tasksArray: {
0: {
subtask: {
1: {
task_name: { $set: 'New value for Task 2' }
}
}
}
}
}
});

update an array nested in an object in an immutable way

So I have this reducer that I would like to update
import { fromJS } from 'immutable';
const initialState = fromJS({
searchParams: {
limit: 100,
page: 1,
order_by: 'name',
filters: {
and_keywords : []
}
},
})
when the action is triggered I would like to push an object into the array of and_keywords. What I have done so far is this
case ADD_KEYWORDS:
return state
.setIn(['searchParams', 'filters', 'and_keywords'], '123');
I also tried
case ADD_KEYWORDS:
return state
.updateIn(['searchParams', 'filters', 'and_keywords'], arr => arr.push('123'))
basing from the documents in https://facebook.github.io, but I can't seem to make it work. No changes have been made after executing this command
Your updateIn version should work:
const Immutable = require('immutable')
const initialState = Immutable.fromJS({
searchParams: {
limit: 100,
page: 1,
order_by: 'name',
filters: {
and_keywords : []
}
},
})
const newState = initialState.updateIn(['searchParams', 'filters', 'and_keywords'], arr => arr.push('123'))
console.log(newState)
Your setIn version code should work as well with a small modification:
const Immutable = require('immutable')
const initialState = Immutable.fromJS({
searchParams: {
limit: 100,
page: 1,
order_by: 'name',
filters: {
and_keywords : []
}
},
})
const newState = initialState.setIn(['searchParams', 'filters', 'and_keywords'], initialState.getIn(['searchParams', 'filters', 'and_keywords']).push('123'))
console.log(newState)
Both should output:
Map { "searchParams": Map { "limit": 100, "page": 1, "order_by": "name", "filters": Map { "and_keywords": List [ "123" ] } } }

Resources