Add/edit item on the dashboard list


First thing to do is to prepare the action and handlers in the modules for DASHBOARD_ADD_ITEM and DASHBOARD_EDIT_ITEM:

Changes in (you can click the diffs image to make it larger):
src/routes/Dashboard/modules/dashboard.js

921_dashboard_reducer_actions

As you can see above, we have added and exported two new actions:

export function dashboardAddItem (value) {
  return {
    type: DASHBOARD_ADD_ITEM,
    payload: value
  }
}

export function dashboardEditItem (value) {
  return {
    type: DASHBOARD_EDIT_ITEM,
    payload: value
  }
}

... then after an action is requested from a Dashboard's component, we handle it with:


  [DASHBOARD_ADD_ITEM]: (state, action) => {
    const mockedId = Math.floor(Date.now() / 1000)
    const newItem = {
      label: action.payload.label,
      id: mockedId
    }

    return {
      ...state,
      dashboardItems: [ ...state.dashboardItems, newItem ]
    }
  },
  [DASHBOARD_EDIT_ITEM]: (state, action) => {
    const { label, editItemIndex: index } = action.payload
    let newItem = {
      ...state.dashboardItems[index],
      label
    }

    const immutableDashboardItems = [
      ...state.dashboardItems.slice(0, index),
      newItem,
      ...state.dashboardItems.slice(index + 1)
    ]
    return {
      ...state,
      dashboardItems: immutableDashboardItems
    }
  }

In both functions above, we are returining a new object with ...state. In the DASHBOARD_EDIT_ITEM handler we fetch editItemIndex from the action.payload object and write into index variable.

The rest of the code should be self-explanatory.

Changes in (you can click the diffs image to make it larger):
src/routes/Dashboard/containers/DashboardContainer.js

922_dashboard_container

Above we have simply added the functions that were created in the Dashboard/modules/dashboard.js file so we import dashboardAddItem and dashboardEditItem .. Now we have to get our function in our statefull Dashboard component:

source of src/routes/Dashboard/components/Dashboard.js

923_dashboard_container

And here we call the parent's method this.props.updateItem for resfresh the redux state:

source of src/components/Dashboard/Dashboard.js

923_dashboard_container

On the above code base, we have added new functions called onChangeInput, onSubmit and itemOnEdit so we need to improve our skeleton as well:


  static propTypes = {
  dashboardItems   : React.PropTypes.array.isRequired,
  updateItem       : React.PropTypes.func.isRequired,
  visitsCount      : React.PropTypes.number.isRequired
}

state = {
  inputValue: '',
  editedItemIndex: null
}

... the inputValue keeps the current value of the text input. In the editedItemIndex we keep an ID of the currently edited item, if there is none in edit then we make it null.

Later we handle all the functions logic with:

onChangeInput = ev => this.setState({ inputValue: ev.target.value })

onSubmit = ev => {
  ev.preventDefault()
  const { inputValue: label, editedItemIndex: editItemIndex } = this.state
  if (label && label.length) {
    this.props.updateItem({ label, editItemIndex })
    this.setState({
      inputValue: '',
      editedItemIndex: null
    })
  } else {
    alert('Value can\'t be empty')
  }
}

itemOnEdit = (editedItemIndex) => () => {
  const editedItem = this.props.dashboardItems[editedItemIndex]
  this.setState({ inputValue: editedItem.label, editedItemIndex })
}

This isn’t anything fancy, so I won't describe it in too much detail (if you think it requires a more detailed description then please mail us at kamil.przeorski@gmail.com).

Final part is the render method:

Changes in (you can click the diffs image to make it larger):
src/components/Dashboard/ListJSX.js

924_dashboard_component_dumb

Above, we have improved our listJSX map function:


  const items = dashboardItems.map((item, i) => {
    const itemJSX = activeIndex === i
      ? <p><b><u>{item.label}</u></b></p>
      : <p>{item.label}</p>

    // Below is the onClick. It is our method this.itemOnEdit.
    return (
      <h4
        key={i}
        onClick={onClick(i)}
        className='dashboard-list-item' >{itemJSX}</h4>
    )
  })

Explanation of onClick callback: In the Dashboard view (/src/components/Dashboard/Dashboard.js) we prepare a method this.itemOnEdit, which returns a method with added before 'hidden' property (i parameter - see above). It's called a closure. Here is an example


... so now it makes an edited item bold and underlined.

Next we have improved the render function of Dashboard component (/src/components/Dashboard/Dashboard.js):

  return (
  <div>
    <h2 className='dashboardContainer' >
      Dashboard visits:
      {' '}
      <span className='dashboard--green' >
        { visitsCount }
      </span>
    </h2>
    <form onSubmit={this.onSubmit}>
      <input
        value={this.state.inputValue}
        type='text'
        placeholder='type here a value'
        className='dashboard-add-item'
        onChange={this.onChangeInput} />
      <input
        value={this.state.editedItemIndex === null ? 'Add New Item To The List' : 'Edit Item'}
        type='submit' />
    </form>
    <ListJSX
      activeIndex={this.state.editedItemIndex}
      dashboardItems={dashboardItems}
      onClick={this.itemOnEdit} />
  </div>
)}

We have added a standard form and inputs - they are communicating with state object the use of callbacks (the value={this.state.inputValue} and onChange={this.onChangeInput} will take care of it). The submit button value is determined by the this.state.editedItemIndex - so if it is a null then that means that a user hasn't clicked any item, yet.

This is the end results of all the changes we've made above:

925_edit_add_anim.gif

Source of the commit's screenshots:
https://github.com/ReactPoland/reactjs-redux-tutorial/commit/chapter-5

React Poland 2017