reduxjs / redux

A JS library for predictable global state management
https://redux.js.org
MIT License
60.88k stars 15.27k forks source link

state changes but doest not call render function of parent component #2190

Closed shabanraza closed 7 years ago

shabanraza commented 7 years ago

Its is my parent component i have dispatch the action from child component and state changes but render function of parent component does not called .

In child component every time when i click on checkbox then i dispatch the action for updating the state in reducer and i received new state in mapStatetoprop but it does not update the UI

const propTypes = {
    teamPlayers : PropTypes.array.isRequired,
    matchesData: PropTypes.object.isRequired,
    isfetchingTeamPlayer: PropTypes.bool.isRequired

};
class CreateTeam extends React.Component {
constructor(props) {
        super(props);
        this.state = {
            selectedPlayerIDs : [],
            count:0,
            isforward:false,
            show:false,
            message:''
        }
        this.toggleChecked = this.toggleChecked.bind(this);
        this.forwardRoute = this.forwardRoute.bind(this);
        this.previousRoute = this.previousRoute.bind(this);

    }

    componentWillMount() {
        this.props.createTeam(this.props.matchID);
    }
 renderPlayerCard() {    console.log(this.props.teamPlayers)
       let count = 0;
        return this.props.teamPlayers.map((player) => {
            if(player.isChecked){
                count+=1;
            }

            let mtype = '';

            return (
                <PlayerCard
                    key={player.id}
                    player={player}
                    mtype={mtype}
                    count={count}
                    selectedPlayerIDs={this.state.selectedPlayerIDs}
                    triggerChanges={this.toggleChecked}
                />

            );

        })
    }

    render () {
        if(!this.props.isfetchingTeamPlayer && !this.props.isPlayersFetching ){
            return <h1>Loading...</h1>;
        }
        console.log("selected5Players"+this.props.selected5Players)
        return(
            <div>
                <div className="card">
                    <div className="container-grey">
                        <div className="timer2">
                            <table className="timer-table2">

                               {
                                   this.props.matchesData.end_time ?
                                       <Counter
                                           endDate={this.props.matchesData.end_time}
                                       /> : null
                               }

                            </table>
                        </div>
                        <table className="team-table2">
                                <tr className="match-team">
                                    <td className='team-logo-box'>
                                        <div className="white-box">
                                            {
                                                this.props.matchesData.hasOwnProperty('teams') ?

                                                    <img className="team-logo2" alt="Team1"
                                                         src={this.props.matchesData.teams.home_team.flag_path}/>
                                                    :null
                                            }
                                            </div>
                                    </td>

                                    <td className="team-name2 left">{this.props.matchesData.teams.away_team.short_name}</td>
                                    <td><img className="versus2" alt="versus" src={vs}/></td>
                                    <td className="team-name2 right">{this.props.matchesData.teams.away_team.short_name} </td>
                                    <td className='team-logo-box'>
                                        <div className="white-box">
                                            <img className="team-logo2" alt="Team2"
                                                 src={this.props.matchesData.teams.away_team.flag_path}/>
                                        </div>
                                    </td>
                                </tr>
                             </table>

                    </div>

                    <div className="player-list">
                        <table className="timer-table2">
                            <tbody>
                                {this.renderPlayerCard()}
                            </tbody>
                        </table>
                    </div>
                </div>

                <div className="foot-container">

                </div>

            </div>

        );
    }
}
CreateTeam.propTypes = propTypes;

const mapStateToProps = (state,ownProps) => {
    // I am getting updated state here but doesn't call the render function//
    console.log("state Changes but does not call the render function of this component")

    return {
        matchID: ownProps.params.teamID,
        selected5Players: state.matchesTeam.selected5Players,
        teamPlayers: selectedCheckedplayers(state),
        matchesData: state.matchesTeam.matchesData,
        isfetchingTeamPlayer: state.matchesTeam.isfetchingTeamPlayer,
        isPlayersFetching: state.matchesTeam.isPlayersFetching
    }
};

const mapDispatchToProps = (dispatch) => bindActionCreators({
    createTeam,
    showSystemMessage,
    navigate: push,
}, dispatch);

export default connect(mapStateToProps, mapDispatchToProps)(CreateTeam);

Below the code of child component where i dispatch the action

here i dispatching the action on every checkbox (handleChange() function ) see below

const propTypes = {
    player: PropTypes.object.isRequired,
};

class PlayerCard extends React.Component {

    handleChange(evt,id,isSelected) {   
          this.props.togglePlayersChecked({"player":this.props.player,"status":!isSelected})
    }

    render() {

        return (
            <tr>
                <td className="batsmen-pic">
                    <img alt='batsmen' className='batsmen-picture' src={this.props.player.photo_url} />
                </td>
                <td className="batsmen-details">
                    <div className="batsmen-name left">
                        <div className="first-name">{this.props.player.name}</div>
                    </div>

                </td>
                <td className="batsmen-checkbox-holder">
                    <div>
                        <input className="batsmen-checkbox"
                               type="checkbox"
                               onChange={event => this.handleChange(event, this.props.player,this.props.player.isChecked)}
                               value={this.props.player.id}
                               checked={this.props.player.isChecked }

                        />
                    </div>
                </td>
            </tr>
        );
    }

}
PlayerCard.propTypes = propTypes;

const mapStateToProps = (state,ownProps) => ({
    selected5Players: state.matchesTeam.selected5Players,

});

const mapDispatchToProps = (dispatch) => bindActionCreators({
    togglePlayersChecked
}, dispatch);

export default connect(mapStateToProps, mapDispatchToProps)(PlayerCard);

Please help me guys i am stuck on this code

markerikson commented 7 years ago

This is a usage question, and should be asked on Stack Overflow instead.

99.9% of the time, this is because you are accidentally mutating data, usually in your reducer. See http://redux.js.org/docs/faq/ReactRedux.html#react-not-rerendering for links to more information on the topic.

shenders13 commented 7 years ago

@markerikson advice "99.9% of the time, this is because you are accidentally mutating data" was spot on. For me it was because I was making a shallow copy of a nested object & the nested values were still being passed by reference.

I made a deep clone by:

const deepCloneOfNestedObject = JSON.parse(JSON.stringify(nestedObject))

markerikson commented 7 years ago

@shenders13 : fwiw, deep cloning is also not a good idea, as you'll be making unnecessary new references, and possibly causing too many re-renders instead. Please see http://redux.js.org/docs/faq/ReactRedux.html#react-rendering-too-often, http://redux.js.org/docs/faq/Performance.html#performance-clone-state, and http://redux.js.org/docs/recipes/reducers/ImmutableUpdatePatterns.html for more info on how to properly update state.

steelx commented 7 years ago

After dispatching an event are you retuning a new state or just modifying existing state ?

joshsiegl1 commented 6 years ago

@markerikson answer solved my problem, if you're trying to apply this concept to an array on your app state this can be a little bit trickier, make sure you're creating a new array first and then concatenating the previous data onto the new array if you're trying to add to an existing array on your app state.