110

I am breaking apart Redux' todo example to try to understand it. I read that mapDispatchToProps allows you to map dispatch actions as props, so I thought of rewriting addTodo.js to use mapDispatchToProps instead of calling dispatch(addTodo()). I called it addingTodo(). Something like this:

import React from 'react';
import {connect} from 'react-redux';
import addTodo from '../actions';

let AddTodo = ({addingTodo}) => {
  let input;
  return (
      <div>
        <form onSubmit={e => {
          e.preventDefault()
          if (!input.value.trim()) {
            return
          }
          addingTodo(input.value)
          input.value = ""
        }}>
          <input ref={node => {
            input = node
          }} />
          <button type="submit">Submit</button>
        </form>
      </div>
  )
}

const mapDispatchToProps = {
  addingTodo: addTodo
}

AddTodo = connect(
  mapDispatchToProps
)(AddTodo)

export default AddTodo

However, when I run the app, I get this error: Error: Invalid value of type object for mapStateToProps argument when connecting component AddTodo.. I never used mapStateToProps to begin with on AddTodo component, so I was not sure what was wrong. My gut feeling says that connect() expects mapStateToProps to precede mapDispatchToProps.

The working original looks like this:

import React from 'react';
import {connect} from 'react-redux';
import addTodo from '../actions';

let AddTodo = ({dispatch}) => {
  let input;
  return (
      <div>
        <form onSubmit={e => {
          e.preventDefault()
          if (!input.value.trim()) {
            return
          }
          dispatch(addTodo(input.value))
          input.value = ""
        }}>
          <input ref={node => {
            input = node
          }} />
          <button type="submit">Submit</button>
        </form>
      </div>
  )
}

AddTodo = connect()(AddTodo)

export default AddTodo

Complete repo can be found here.

So my question is, is it possible to do mapDispatchToProps without mapStateToProps? Is what I am trying to do an acceptable practice - if not, why not?

iofjuupasli
  • 3,818
  • 1
  • 16
  • 17
Iggy
  • 5,129
  • 12
  • 53
  • 87

1 Answers1

172

Yes, you can. Just pass null as first argument:

AddTodo = connect(
    null,
    mapDispatchToProps
)(AddTodo)

Yes, it's not just acceptable practice, it's recommended way to trigger actions. Using mapDispatchToProps allows to hide the fact of using redux inside your react components

iofjuupasli
  • 3,818
  • 1
  • 16
  • 17
  • 8
    But can I use mapStateToProps without mapDispatchToProps the same way? – Velizar Andreev Kitanov Jul 06 '18 at 11:07
  • 7
    @VelizarAndreevKitanov yes – iofjuupasli Jul 06 '18 at 14:08
  • 28
    When using only `mapStateToProps`, one may omit the second argument to `connect`. It's not necessary to pass `null` – theUtherSide Jul 11 '18 at 20:19
  • 1
    For reverse case mapStateToProps without mapDispatchToProps no need to pass null. only pass mapStateToProps – Rajesh N Aug 09 '18 at 05:13
  • Why `null` and not `undefined`? Edit: Apparently both are ok according to the manual: https://github.com/reduxjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options – Lars Nyström Oct 18 '18 at 13:35
  • @LarsNyström for me, semantics of null works better here. It's like "definitely nothing" – iofjuupasli Oct 18 '18 at 13:44
  • 1
    For me it's the other way around. From the receivers point of view, `function myfunc(prop) { return prop === undefined }` would return true both for `myfunc()` and `myfunc(undefined)`. Passing undefined is like not passing anything at all. Whereas passing null is like, "I passed you something, but it's actually nothing". – Lars Nyström Oct 19 '18 at 07:51
  • @LarsNyström "Passing undefined is like not passing anything at all", that's not true. Certain libraries depend on `arguments.length` to see how many arguments you've provided, and how you meant to invoke the function. `(function(f) { return arguments.length; })(undefined) // 1`, `(function(f) { return arguments.length; })() // 0`. Interesting blog about this subject: https://blog.sentry.io/2016/02/03/wrap-javascript-functions – s.meijer Nov 19 '18 at 11:43
  • 2
    `connect` should have accepted a object literal as a parameter instead of list of arguments. – Mahdi Pedram Nov 24 '18 at 23:42
  • 1
    @theUtherSide it should be noted that when using TypeScript passing `null` as the second argument to `connect` (in lieu of `mapDispatchToProps`) causes type checking to fail) so it is necessary to simply use `connect(mapStateToProps)`. Tested with `TS 3.9.6` and `Redux 4.0.5` – Marcus Junius Brutus Feb 06 '21 at 10:41