0

I'm using react with redux and I'm having problems with a component connected to a list from a store. I have a list that whenever I click it adds an object or deletes it from the list with the action select, and I have a component connected to the state getting the list and rendering differently if the list is empty, length == 1 or >= 1. The list starts empty and when I add the first object the action is carried and the component re-renders, if I trigger the action again the component never renders again but I can see in the console that the next state has all the selected elements. What am I missing?

//Component that executes action:


const mapDispatchToProps = dispatch => {
  return {
select: selected => dispatch(select(selected))
  };
};

class AnimatedlistComponent extends Component {

    constructor(props) {
        super(props)
        this.state = {selected: {}}
    }

    onItemClick(d) {
        console.log("selected " + d)
        let selected = this.state.selected
        if (this.state.selected[d.id]) {
            delete selected[d.id]
            this.props.select(selected)
        } else {
           selected[d.id] = d
           this.props.select(selected)
        }
    }

    render() {
     
        const childElements = this.props.detectors.map(u => {
            return (
                <ListGroupItem id={'list' + u.id} className='detectorlistElement' key={u.id} style={elementStyle}
                               onClick={() => this.onItemClick(u)}>
                    <Glyphicon glyph="tag"/> {u.name}
                </ListGroupItem>

            );
        });

        return (
            <div>
                <p style={headerStyle}><b>Dector list</b></p>
                <ListGroup id={"detectorList"}>
                    {childElements}
                </ListGroup>
            </div>
        );
    }

}
export default connect(null,mapDispatchToProps)(AnimatedlistComponent)



   // Component that receives prop:

function mapStateToProps(state) {
    return {Selected: state.reducer.Selected }
}
@connect(mapStateToProps)
class MiddlePanel extends Component {
  constructor(props) {
    super(props);

  }
componentWillReceiveProps(){
      console.log("receive new props")
}
  render() {
      console.log("render middle")
    const Selected = this.props.Selected;
    let middlepanel
    if (Object.keys(detectorsSelected).length >1) {
      middlepanel = <div>mas que uno</div>;
    } else if (Object.keys(detectorsSelected).length === 0){
      middlepanel = <div>Ninguno</div>
    }else if (Object.keys(detectorsSelected).length === 1){
        middlepanel = <div>uno</div>
    }
    return (
      <div>
        {middlepanel}
      </div>
    );
  }
}

  //  Action:

export function select(Selected) {
    return {
        type: types.SELECT,
        Selected: Selected
    }
};
   // Reducer:

Reducer(state = {
                Selected: {}
            }, action = null) {
switch (action.type) {
case types.SELECT:
      return Object.assign({}, state, {Selected: action.Selected});
        
        default:
            return state;
    }
};
alvcarmona
  • 29
  • 11
  • First you action should have a type and a payload, so instead of `detectorsSelected` in your action, write `payload: { Selected }`. Than, in your reducer, try something like this `return Object.assign({}, state, {Selected: action.payload.Selected});` – kuby Jul 11 '18 at 12:09
  • @kuby Doesn't solve anything, still getting the correct new state from the action but not re rendering :( – alvcarmona Jul 11 '18 at 12:27
  • There is some missing code, the addition in the component, it looks like the action receives an array, is that the same array from the props? where is the addition happening? arrays are mutable so you need to create a new array everytime.. – Matan Bobi Jul 11 '18 at 12:42
  • @alvcarmona where is dispatch action code ? – Javed Shaikh Jul 11 '18 at 13:04
  • @javed I just added the code from the component that triggers the action. Thanks! – alvcarmona Jul 11 '18 at 14:30
  • @MatanBobi I just added the code from the component that triggers the action. Thanks! – alvcarmona Jul 11 '18 at 14:31
  • @alvcarmona add console.log and print selected in mapDispatchToProps – Javed Shaikh Jul 11 '18 at 14:34
  • problem is seams that you are getting selected in your props but not in state. ? correct me if i am wrong. – Javed Shaikh Jul 11 '18 at 14:38
  • @javed this.props.select(selected) calls the action, passes a dict as parameter which in the next state is updated correctly. I think the problem is somewhere in the connect of both components. Really bothers me that the first action trigger behaves as I wanted but not the next ones – alvcarmona Jul 11 '18 at 14:47
  • @alvcarmona can you please paste your code on any online editor so that i can correct it. like stackblitz. – Javed Shaikh Jul 11 '18 at 14:58
  • @alvcarmona in your MiddlePanel you are using this.props.Selected; in your render might that the cause. component will re-render only if state changed not a props. – Javed Shaikh Jul 11 '18 at 15:05

1 Answers1

1

I see a few problems with your code, I'll point them out:
1. Inside AnimatedlistComponent you have selected object it in the state but you're not using in the render function and it looks like there's actually no need in it. If you omitted some parts of the render method and you are using the selected object from the state then you should be setting the state once the object is changed.
2. It looks like your AnimatedlistComponent component is half controlled. You're keeping it's data in two places, once in the app state (when using redux to update other components) and once in the component, that isn't a good practice. You should be saving it in one place and keep it synchronized.
3. The problem that causes the issue you're experiencing is actually because you're keeping a reference to the original selected object and not copying it. You should be doing something like: return Object.assign({}, state, {Selected: {...action.Selected}}); in your reducer to get immutability.

Feel free to comment if my response isn't helpful enough.

Matan Bobi
  • 2,693
  • 1
  • 15
  • 27