1
votes

I'm using react and react-redux. I used mapstatetoprops and mapdispatchtoprops to update view of my react component. Except re-render doesn't work after redux store changed, everything works fine. Action dispatch works fine, reducer works fine, I can console.log store state and check difference. At first, I used useDispatch and useSelector and everything worked fine. But I'm changing it to mapdispatchtoprops and mapstatetoprops to merge my code into my project teammate's code.

I tried to put this.props.(whatineed) directly in my render()'s return in component. As I understand, through mapstatetoprops, store state should be passed into my component's props.

import React, { Component } from 'react';
import { ToggleButton, ToggleButtonGroup } from 'react-bootstrap';
import { useSelector, useDispatch } from 'react-redux';
import { checked, notchecked } from '../../../actions';
import { connect } from "react-redux";
import local from './address';
import './index.css';
const mapStateToProps = state => {
    return {
      localsel : state.selectedLocal.locals
    }
  }
let mapDispatchToProps = (dispatch) => {
    return {
        check: (btn) => dispatch(checked(btn)),
        uncheck: (btn) => dispatch(notchecked(btn))
    }
}
class Seoul extends Component {
    constructor(props){
        super(props)
    }
    render(){
        var btnclicked = (e) => {
            let btnname = e.target.parentNode.getAttribute('id');
            if (e.target.checked) {
                console.log('checked');
                this.props.check(btnname);
            };
            if (!e.target.checked) {
                console.log('not checked');
                this.props.uncheck(btnname);
            };
            // HERE IS WHERE I CAN CHECK THE PASSED STORE STATE
            console.log(this.props.localsel);
            // -------------------------------------------------
        }
        return (
            <div className='localdiv localdiv1'>
                // HERE IS WHERE I WANT TO SEE MY STORE STATE
                {this.props.localsel.map(val=>{
                    return <h1>{val}</h1>
                })}
                // --------------------------------------------
                <ToggleButtonGroup className='togglebtngrp' type="checkbox">
                    <ToggleButton className='togglebtn0' onChange={btnclicked} variant="outline-secondary" value={0} id="entireseoul">Entire Seoul</ToggleButton>
                    {local.Seoul.map((value, index) => {
                        return (<ToggleButton key={index} className='togglebtn' onChange={btnclicked} variant="outline-primary" value={index + 1} id={value}>{value}</ToggleButton>)
                    })}
                </ToggleButtonGroup>
            </div>
        );
    }
}
export default connect(mapStateToProps, mapDispatchToProps)(Seoul);

this component is exported in parent component, which is

import React, { Component } from 'react';
import { Jumbotron } from 'react-bootstrap';
import { Gyeongi, Incheon, Busan, Daegue, Daejeon, Sejong, Gwangju, Ulsan, Gangwon, Gyungnam, Gyungbuk, Jeonnam, Jeonbuk, Choongnam, Choongbuk, Jeju, Othercountry } from './Locals';
import Seoul from './Locals';
import './Detailsrch.css';

class Detailsrch extends Component{
    render(){
        var localselect = (e) => {
            let selector = document.getElementsByClassName('locals');
            let selector_local = document.getElementsByClassName('localdiv');
            let i = 0;
            for (let j = 0; j < selector_local.length; j++) {
                selector_local[j].style.display = 'none';
            }
            let boxclass = e.target.getAttribute('name');
            if (boxclass) document.getElementsByClassName(boxclass)[0].style.display = 'block';
            while (selector[i]) {
                selector[i].className = 'locals';
                i++;
            }
            if (e.target.className == 'localtext') {
                e.target.parentElement.className = 'locals localclick';
            } else {
                e.target.className = 'locals localclick';
            }
        }
        return (
            <Jumbotron className='searchjumbo'>
                <p>Locals</p>
                <Seoul />
                <Gyeongi />
                <Incheon />
                <Busan />
                <Daegue />
                <Daejeon />
                <Sejong />
                <Gwangju />
                <Ulsan />
                <Gangwon />
                <Gyungnam />
                <Gyungbuk />
                <Jeonnam />
                <Jeonbuk />
                <Choongnam />
                <Choongbuk />
                <Jeju />
                <Othercountry />
                <hr className='firsthr' />
                <p>type</p><hr />

                <p>career</p><hr />

                <p>country</p><hr />

                <p>sex</p>
            </Jumbotron>
        );
    }
};

export default Detailsrch;

here's my reducer

import { combineReducers } from 'redux';
const initialstate = {
    locals: []
}
const localSelector = (state = initialstate, action) => {
    switch(action.type){
        case 'CHECKED':
            if(action.payload){
                var arr = state.locals;
                arr.push(action.payload);
                return {
                    ...state,
                    locals: arr
                };
            } else {
                return state;
            }
        case 'NOTCHECKED':
            if(action.payload){
                var arrnum = state.locals.indexOf(action.payload);
                var arr = state.locals;
                arr.splice(arrnum, 1);
                return {
                    ...state,
                    locals: arr
                };
            } else {
                return state;
            }
        default:
            return state;
    }
};

const rootReducer = combineReducers({
    selectedLocal: localSelector
});
export default rootReducer;

I expect when props value changes, component will re-render and I will see the change in the browser. Props value has changed, but nothing happens in browser.

3
Post your reducer also.ravibagul91
@ravibagul91 I add it at the bottommomo1108

3 Answers

2
votes

You are mutating the redux state as below

var arr = state.locals;
arr.push(action.payload);

The redux state should be immutable. You can have a look at here for some tips on how to update the redux store in reducer.

https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns

0
votes

I can't see Detailsrch imported in Seoul but it's vice versa Seoul is imported in Detailsrch and as per the code and comments i can see this.props.localsel is changing and this is used in Seoul so render method of Seoul will be called and since there is no mapping or usage of this.props.localsel in Detailsrch so render method will not be called.

So if you want to re-render Detailsrch you need to change the mapping of this.props.localsel from Seoul to Detailsrch and pass this value as props to Seoul it should be working. If still issue exists please post your code to sandbox/codepen/jsfiddle so that we can reproduce.

0
votes

Probably you are suffering from something like this post mentioned. One trick to make your component to get the changes from the store (When passing state value from the store as a prop) is to make a deep clone of your props (Or the prop in which you want to get the change), for this you could use JSON:

JSON.parse(JSON.stringify(propToClone));

Hope this helps.

P.S.: Don't clone props that are functions, because JSON will erase/ignore them.