1

I have a Ruler component and when I click on it, it creates horizontal ruler lines for me by adding 1 more to my redux store. So far I have no problems when I am adding them, but I also want to remove these rules when I double click on them, I send the item index through the action to my reducer, where I run an array.slice to remove just the rule that I double clicked on. But no matter what, it just pops the last rule for me.

I know that this is somehow happening when I am rendering the component, since when I console.log the array in the reducer, the correct element is removed from it, but it renders differently. Here are the codes I have used:

My Ruler component:

class Rulers extends Component {
    render() {
        const { mousePosition, wheelY, dragY, rulerHGuides } = this.props;

        const ruleH = rulerHGuides.map((ruleH, i)=><RuleH index={i} wheelY={wheelY} dragY={dragY} key={i} {...ruleH} /> )

        return (
            <div>
                <div className="rules">
                    { ruleH }
                </div>
                <div className="ruler-horizontal" onClick={e=>{ store.dispatch( actions.rulerHGuides({top: mousePosition.y}) ) }}>
                    <span className="mouse-follow-h" style={{transform: `translateX(${mousePosition.x}px)`}}></span>
                    <svg id="svgH" xmlns="http://www.w3.org/2000/svg"></svg>
                </div>
            </div>
        )
    }
}

export default connect(state=>{
    const { mousePosition, rulerHGuides } = state;
    return { mousePosition, rulerHGuides }
})(Rulers)

The RuleH component

class RuleH extends Component {
    constructor(props) {
        super(props)

        this.state = {
            rulezHDrag: 0,
            top: 0
        }
    }
    render() {
        const { wheelY, dragY, index, id } = this.props;
        const { rulezHDrag, top } = this.state;

        return (
            <DraggableCore onDrag={this.handleRuleH.bind(this)} enableUserSelectHack={false}>
                <span id={id} className="ruleH" style={{top, transform: `translate3d(0, ${(-wheelY) + dragY + rulezHDrag}px, 0)`}}
                    onDoubleClick={e=>{
                        store.dispatch( actions.removeRulerHGuide( index ) )
                        console.log(index);
                    }}
                ></span>
            </DraggableCore>
        )
    }
    componentDidMount() {
        this.setState({
            top: `${this.props.top - ((-this.props.wheelY) + this.props.dragY)}px`
        })
    }
    handleRuleH(e: MouseEvent, data: Object){
        const { rulezHDrag } = this.state;
        this.setState({
            rulezHDrag: rulezHDrag + data.deltaY
        })
    }
}

My Action Creator:

// Ruler guides
// ==========================================
export const rulerHGuides = (hGuides) => {
    return {
        type: 'RULER_H_GUIDES',
        hGuides
    }
}
export const removeRulerHGuide = (index) => {
    return {
        type: 'REMOVE_RULER_H_GUIDE',
        index
    }
}

My Reducer:

export const rulerHGuides = (state = [], action) => {
    switch (action.type) {
        case 'RULER_H_GUIDES':
            return [
                ...state,
                action.hGuides
            ]
        case 'REMOVE_RULER_H_GUIDE':
            return [
                ...state.slice(0,action.index),
                ...state.slice(action.index + 1)
            ]
        default:
            return state;
    }
}

I have tried using array.filters instead of array.slice, but that is not the problem, the issue, whatever it is, is happening inside my component where I am mapping my rulerHGuides array.

1 Answer 1

1

Instead of using indices please use some form of unique id and assign it to a rule at the creation time. When you use indices as keys for React components, the same index is used as a key after item is removed which breaks the re-rendering optimization.

For example, having indices 0, 1, 2, 3 and removing the item at 2 you are left with 0, 1, 2 - there you can see the last key is removed which is causing the issue you described.

Sign up to request clarification or add additional context in comments.

1 Comment

Basically, as you said, the whole thing was caused because I passed the array index as the component key. So what I did was to create a counter outside of my component, then assign that number as the key add 1 to the counter and continue. This way, it worked perfectly fine. So thank you very much.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.