2

I'm new to React and Redux so I'm probably making a rookie mistake here.

I have an array of values visible on the page. A user can then change a value which is dispatched to the store, listened for and then the page is updated. The catch I'm finding is only the first listen works. (eg: only the first change is picked up by the react-redux listener and updated on the page.

Note: this is the first time I've worked with an array in React/Redux - Boolean and strings work fine for me.

Component:

import React from 'react'
import { Card } from 'material-ui'
import Close from 'material-ui-icons/Close'
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import {
    setHfcLiveModemPerfChartComponentOptionsStatus,
    setHfcLiveModemPerfChartComponentThresholdStatus
} from "../actions"



class HfcLiveModemPerfChartOptions extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            popUpThreshold: [-10000, 25, 25, 40, 40, 60, 60, 80, 80, 10000]
        }
    }


    handleCloseOptions() {
        this.props.HfcLiveModemOptionsClose();
    }


    handleThresholdChange(type, event) {
        this.state.popUpThreshold[type] = event.target.value;
        this.props.HfcLiveModemThresholdChange(this.state.popUpThreshold);
    }


    render() {

        console.log('plz rerender now...');

        if(this.props.contentRender) {      // Conditional Rendering

            console.dir(this.state.popUpThreshold);

            return (
                <div>
                    <Card id="HfcLiveModemPerfChartOptions" className="cardOptionsContainer">
                        <h3 id="optionsTitle">Component Options</h3>
                        <Close
                            className="optionsClose"
                            onClick={this.handleCloseOptions.bind(this)}
                        />

                        <h4 id="optionsSubTitle">Admin Threshold ColorBands</h4>
                        <table>
                            <tbody>
                                <tr>
                                    <td><div className="colorBox colorBoxRed"></div></td>
                                    <td className="colorBandName">Red Low</td>
                                    <td>
                                        <input
                                            type="number"
                                            placeholder="-10000"
                                            className="thresholdValue"
                                            value={this.state.popUpThreshold[0]}
                                            onChange={this.handleThresholdChange.bind(this, 0)}
                                        />
                                    </td>
                                    <td>
                                        <input
                                            type="number"
                                            placeholder="-15"
                                            className="thresholdValue"
                                            value={this.state.popUpThreshold[1]}
                                            onChange={this.handleThresholdChange.bind(this, 1)}
                                        />
                                    </td>
                                </tr>
                                <tr>
                                    <td><div className="colorBox colorBoxOrange"></div></td>
                                    <td className="colorBandName">Orange Low</td>
                                    <td>
                                        <input
                                            type="text"
                                            placeholder="-15"
                                            className="thresholdValue"
                                            value={this.state.popUpThreshold[2]}
                                            onChange={this.handleThresholdChange.bind(this, 2)}
                                        />
                                    </td>
                                    <td>
                                        <input
                                            type="text"
                                            placeholder="0"
                                            className="thresholdValue"
                                            value={this.state.popUpThreshold[3]}
                                            onChange={this.handleThresholdChange.bind(this, 3)}
                                        />
                                    </td>
                                </tr>
                                <tr>
                                    <td><div className="colorBox colorBoxGreen"></div></td>
                                    <td className="colorBandName">Green</td>
                                    <td>
                                        <input
                                            type="text"
                                            placeholder="0"
                                            className="thresholdValue"
                                            value={this.state.popUpThreshold[4]}
                                            onChange={this.handleThresholdChange.bind(this, 4)}
                                        />
                                    </td>
                                    <td>
                                        <input
                                            type="text"
                                            placeholder="10"
                                            className="thresholdValue"
                                            value={this.state.popUpThreshold[5]}
                                            onChange={this.handleThresholdChange.bind(this, 5)}
                                        />
                                    </td>
                                </tr>
                                <tr>
                                    <td><div className="colorBox colorBoxOrange"></div></td>
                                    <td className="colorBandName">Orange High</td>
                                    <td>
                                        <input
                                            type="text"
                                            placeholder="10"
                                            className="thresholdValue"
                                            value={this.state.popUpThreshold[6]}
                                            onChange={this.handleThresholdChange.bind(this, 6)}
                                        />
                                    </td>
                                    <td>
                                        <input
                                            type="text"
                                            placeholder="15"
                                            className="thresholdValue"
                                            value={this.state.popUpThreshold[7]}
                                            onChange={this.handleThresholdChange.bind(this, 7)}
                                        />
                                    </td>
                                </tr>
                                <tr>
                                    <td><div className="colorBox colorBoxRed"></div></td>
                                    <td className="colorBandName">Red High</td>
                                    <td>
                                        <input
                                            type="text"
                                            placeholder="15"
                                            className="thresholdValue"
                                            value={this.state.popUpThreshold[8]}
                                            onChange={this.handleThresholdChange.bind(this, 8)}
                                        />
                                    </td>
                                    <td>
                                        <input
                                            type="text"
                                            placeholder="10000"
                                            className="thresholdValue"
                                            value={this.state.popUpThreshold[9]}
                                            onChange={this.handleThresholdChange.bind(this, 9)}
                                        />
                                    </td>
                                </tr>
                            </tbody>
                        </table>

                    </Card>
                </div>
            )
        }else{return false}
    }
}



HfcLiveModemPerfChartOptions.propTypes = {
    contentRender: PropTypes.bool,
    popUpThreshold: PropTypes.array

};

const mapStateToProps = (state) => {
    return {
        contentRender: state.setHfcLiveModemPerfChartComponentOptionsStatus.setHfcLiveModemPerfChartComponentOptionsStatusState,
        popUpThreshold: state.setHfcLiveModemPerfChartComponentThresholdStatus.setHfcLiveModemPerfChartComponentThresholdStatusState
    }
}

const mapDispatchToProps = (Dispatch) => {
    return({
        HfcLiveModemOptionsClose: () => Dispatch(setHfcLiveModemPerfChartComponentOptionsStatus),
        HfcLiveModemThresholdChange: (value) => Dispatch(setHfcLiveModemPerfChartComponentThresholdStatus(value))
    })
}

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

Action/Active Creator:

export const HFC_LIVE_MODEM_PERF_CHART_COMPONENT_THRESHOLD_STATUS = 'HFC_LIVE_MODEM_PERF_CHART_COMPONENT_THRESHOLD_STATUS'

export function setHfcLiveModemPerfChartComponentThresholdStatus(value) {
    return {
        type: 'HFC_LIVE_MODEM_PERF_CHART_COMPONENT_THRESHOLD_STATUS',
        value
    }
}

Reducer:

// HFC Live Modem Performance Chart Threshold Reducer
const setHfcLiveModemPerfChartComponentThresholdStatusInitState = {setHfcLiveModemPerfChartComponentThresholdStatusState: []};
const setHfcLiveModemPerfChartComponentThresholdStatus = (state = setHfcLiveModemPerfChartComponentThresholdStatusInitState, action) => {
    switch (action.type) {
        case HFC_LIVE_MODEM_PERF_CHART_COMPONENT_THRESHOLD_STATUS:
            let newState = {};
            newState['setHfcLiveModemPerfChartComponentThresholdStatusState'] = action.value;
            return Object.assign({}, state, newState);
        default:
            return state;
    }
}

// Combine & Export Reducers
export default combineReducers({
    setHfcLiveModemPerfChartComponentThresholdStatus
})

DOM Element Image: enter image description here

Redux View: enter image description here

Potentially its related to how I'm storing the array in Redux.

Potentially I should stringify the array before storing?

** Update: The dispatch/listener works the first time, however thereafter the values are updating in Redux but the listener doesn't pickup the changes and therefore the page doesn't update..

thanks so much!

Adam
  • 19,932
  • 36
  • 124
  • 207
  • You should not mutate state directly. You need to use `setState`, but in your case you need not store the array in React state at all. – Dane Oct 13 '17 at 04:09
  • can you explain how I'm directly mutating the state? When you say not store the array in React do you mean store 10 separate values? Note: I need the values in Redux as other components will listen/need the numbers when they change. – Adam Oct 13 '17 at 04:15
  • This: `this.state.popUpThreshold[type] = event.target.value;` should not be done. See https://stackoverflow.com/questions/26253351/correct-modification-of-state-arrays-in-reactjs – Dane Oct 13 '17 at 04:18
  • Good call - some how i missed that... I'll update but not sure if that will fix the issue - thankyou for helping :) – Adam Oct 13 '17 at 04:19

1 Answers1

2

The problem is React performs only a shallow checking of props each time. Your Redux store shape must me more flat than having an array inside an object. Try to store the array as such inside the Redux store. And dispatch the action with just the index and value of the changed value, not the whole array. Then do something like:

handleThresholdChange(type, event) {
    this.state.popUpThreshold[type] = event.target.value;
    this.props.HfcLiveModemThresholdChange(type, event.target.value);
}

export function setHfcLiveModemPerfChartComponentThresholdStatus(index, value) {
    return {
        type: 'HFC_LIVE_MODEM_PERF_CHART_COMPONENT_THRESHOLD_STATUS',
        index,
        value
    }
}

const setHfcLiveModemPerfChartComponentThresholdStatusInitState = [];

const setHfcLiveModemPerfChartComponentThresholdStatus = (state = setHfcLiveModemPerfChartComponentThresholdStatusInitState, action) => {
    switch (action.type) {
        case HFC_LIVE_MODEM_PERF_CHART_COMPONENT_THRESHOLD_STATUS:
          return Object.assign(
            {},
            state,
            { [action.index]: action.value }
          );
        default:
            return state;
    }
}

Just tell me if it isn't working.

Dane
  • 9,242
  • 5
  • 33
  • 56
  • Hey - thanks for writing. So your saying to store each value separately? And this is to avoid nested structures like arrays which React/Redux doesn't check for - flat issue... – Adam Oct 13 '17 at 04:57
  • Not store each value separately. The Redux store will contain an array instead of array inside an object. I said when a value changes, you need to **dispatch** an action only with the changed value instead of the whole array... It makes more sense. – Dane Oct 13 '17 at 05:00
  • yes it makes sense - I need to do more testing (as always) but I understand much better now - thank you for writing :) – Adam Oct 13 '17 at 05:02