2

I have a React Redux app which gets data from my server and displays that data.

I am displaying the data in my parent container with something like:

render(){
    var dataList = this.props.data.map( (data)=> <CustomComponent key={data.id}> data.name </CustomComponent>)

    return (
        <div>
           {dataList}
        </div>
    )
}

When I interact with my app, sometimes, I need to update a specific CustomComponent.

Since each CustomComponent has an id I send that to my server with some data about what the user chose. (ie it's a form)

The server responds with the updated object for that id.

And in my redux module, I iterate through my current data state and find the object whose id's

export function receiveNewData(id){
    return (dispatch, getState) => {
        const currentData = getState().data
        for (var i=0; i < currentData.length; i++){
            if (currentData[i] === id) {
                const updatedDataObject = Object.assign({},currentData[i], {newParam:"blahBlah"})
                allUpdatedData = [
                    ...currentData.slice(0,i),
                    updatedDataObject,
                    ...currentData.slice(i+1)
                ]
                dispatch(updateData(allUpdatedData))
                break
            }
        }
    }
}


const updateData = createAction("UPDATE_DATA")

createAction comes from redux-actions which basically creates an object of {type, payload}. (It standardizes action creators)

Anyways, from this example you can see that each time I have a change I constantly iterate through my entire array to identify which object is changing.

This seems inefficient to me considering I already have the id of that object.

I'm wondering if there is a better way to handle this for React / Redux? Any suggestions?

2 Answers 2

3

Your action creator is doing too much. It's taking on work that belongs in the reducer. All your action creator need do is announce what to change, not how to change it. e.g.

export function updateData(id, data) {
    return {
        type: 'UPDATE_DATA',
        id: id,
        data: data
    };
}

Now move all that logic into the reducer. e.g.

case 'UPDATE_DATA':
    const index = state.items.findIndex((item) => item.id === action.id);
    return Object.assign({}, state, {
        items: [
            ...state.items.slice(0, index),
            Object.assign({}, state.items[index], action.data),
            ...state.items.slice(index + 1)
        ]
    });

If you're worried about the O(n) call of Array#findIndex, then consider re-indexing your data with normalizr (or something similar). However only do this if you're experiencing performance problems; it shouldn't be necessary with small data sets.

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

Comments

1

Why not using an object indexed by id? You'll then only have to access the property of your object using it.

 const data = { 1: { id: 1, name: 'one' }, 2: { id: 2, name: 'two' } }

Then your render will look like this:

render () {

  return (
    <div>
     {Object.keys(this.props.data).forEach(key => {
       const data = this.props.data[key]
       return <CustomComponent key={data.id}>{data.name}</CustomComponent>
     })}
    </div>
  )
}

And your receive data action, I updated a bit:

export function receiveNewData (id) {
  return (dispatch, getState) => {
    const currentData = getState().data

    dispatch(updateData({
      ...currentData,
      [id]: {
        ...currentData[id],
        { newParam: 'blahBlah' }
      }
    }))
  }
}

Though I agree with David that a lot of the action logic should be moved to your reducer handler.

Comments

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.