调度动作错误,无法读取道具

时间:2017-05-29 11:15:13

标签: reactjs redux

我正在尝试发送动作以显示模态。

我得到的错误无法读取null的道具。

    <div className="cell">
                <button onClick={this.openAchievementModal} className="modal-button">+ Add Achievement</button>
    </div>

点击处理程序:

openAchievementModal(){
    this.props.showModal({
      type: 'SHOW_MODAL',
      modalType: 'DELETE_POST',
      modalProps: {
        postId: 42
    }
  })
}

我是如何用道具发送行动的?

更新:我有2个减速器,一个用于管理我的数据:

import {ADD_ACHIEVEMENT, ADD_ENJOYMENT } from '../constants/ActionTypes'

const initialState = [
  {
    date: "Fri 1st",
    enjoyments: [],
    achievements: [],
    id: 0
  },
  {
    date: "Sat 2nd",
    enjoyments: ['Football', 'Rugby'],
    achievements: ['Tennis', 'Football'],
    id: 1
  },
  { 
    date: "Sun 3rd",
    enjoyments: [],
    achievements: [],
    id: 2
  },
  {
    date: "Mon 4th",
    enjoyments: [],
    achievements: [],
    id:3
  },
  {
    date: "Tue 5th",
    enjoyments: [],
    achievements: [],
    id: 4
  },
  "isOpen": false
]

export default function days(state = initialState, action) {
  switch (action.type) {

   case ADD_ACHIEVEMENT:
      return state.map(item => {
          if (item.id === action.id) {
              return Object.assign({}, item, {
                achievements: [
                ...item.achievements,
                action.text,
                ]
              });
          }

        return item;
    });
    case ADD_ENJOYMENT:
      return state.map(item => {
        if (item.id === action.id) {
          return Object.assign({}, item, {
            enjoyments: [
              ...item.enjoyments,
              action.text,
            ]
          });
        }

        return item;
    });
    default:
      return state
  }
}

另一个管理我的模态:

import {SHOW_MODAL, HIDE_MODAL } from '../constants/ActionTypes'


import React from 'react';
import {connect} from 'react-redux';

import * as actions from '../actions';

import AddAchievementModal from '../components/modals/AddAchievementModal.js';
import RemoveAchievementModal from '../components/modals/RemoveAchievementModal.js';

const initialState = {
  modalType: null,
  modalProps: {}
}


function modal(state = initialState, action) {
  switch (action.type) {
    case 'SHOW_MODAL':
      return {
        modalType: action.modalType,
        modalProps: action.modalProps
      }
    case 'HIDE_MODAL':
      return initialState
    default:
      return state
  }
}

在我的容器组件中,我有以下代码:

import React, { Component } from 'react'
import {connect} from 'react-redux'
import * as actions from '../actions'

class Achievement extends Component {

  openAchievementModal(){
        this.props.showModal({
          type: 'SHOW_MODAL',
          modalType: 'DELETE_POST',
          modalProps: {
            postId: 42
        }
    })
  }

  render() {
        var options = [
          { value: 1, label: 'Play Music' },
          { value: 2, label: 'Football' }
        ];

    let listItems

    if(this.props.day.achievements) {
       listItems = this.props.day.achievements.map((achievement) => (
           <div className="cell" key={achievement + "_achievements"}>
             {achievement}
         <button onClick={this.openRemoveModal} className="modal-button">+ remove Achievement</button>
           </div>
       ))
    }

    return (
         <span key={this.props.day + "span"}>
                {listItems}

                <div className="cell">
                    <button onClick={this.openAchievementModal} className="modal-button">+ Add Achievement</button>
                </div>

      </span>
    )

    }
}

function mapStateToProps(state, ownProps) {
  return {
    days: state.days
  };
}

export default connect(mapStateToProps)(Achievement);

我的ModelRoot组件:

import AddAchievementModal from '../components/modals/AddAchievementModal.js';
import RemoveAchievementModal from '../components/modals/RemoveAchievementModal.js';

const MODAL_COMPONENTS = {
  'REMOVE_ACHIEVEMENT': RemoveAchievementModal,
  'ADD_ACHIEVEMENT': AddAchievementModal,
  /* other modals */
}

const ModalRoot = ({ modalType, modalProps }) => {
  if (!modalType) {
    return <span /> // after React v15 you can return null here
  }

  const SpecificModal = MODAL_COMPONENTS[modalType]
  return <SpecificModal {...modalProps} />
}

export default connect(
  state => state.modal
)(ModalRoot)

3 个答案:

答案 0 :(得分:1)

openAchievementModal未绑定到组件实例,因为JavaScript方法默认情况下不绑定类方法。请在构造函数中手动绑定它:

constructor() {
  super()
  this.openAchievementModal = this.openAchievementModal.bind(this)
}

或者在渲染功能中:

<button onClick={this.openAchievementModal.bind(this)} ... />

您也可以使用箭头函数语法来保留词汇(如bzekiunat所示):

<button onClick={() => { this.openAchievementModal() } } ... />

或者,如果您已经转换了代码,请使用class properties和箭头功能(注意= () =>代替()):

openAchievementModal = () => {
  this.props.showModal({
    type: 'SHOW_MODAL',
    modalType: 'DELETE_POST',
    modalProps: {
      postId: 42
    }
  })
}

最终代码:

class Achievement extends Component {
  constructor() {
    super()
    this.openAchievementModal = this.openAchievementModal.bind(this)
  }

  render() {
    ...
    <button onClick={this.openAchievementModal} className="modal-button">+ Add Achievement</button>
    ...
  }
}

请参阅以下示例了解不同的绑定方法:

&#13;
&#13;
class ConstructorBoundExample extends React.Component {
  constructor() {
    super()
    this.openAchievementModal = this.openAchievementModal.bind(this)
  }
  
  openAchievementModal() {
    console.log(this.props)
    this.props.openModal("test")
  }

  render() {
    return (
      <button onClick={this.openAchievementModal} className="modal-button">Constructor bound - works</button>
    )
  }
}

class InlineBoundExample extends React.Component {
  openAchievementModal() {
    console.log(this.props)
    this.props.openModal("test")
  }

  render() {
    return (
      <button onClick={this.openAchievementModal.bind(this)} className="modal-button">Inline bound - works</button>
    )
  }
}

class FunctionBoundExample extends React.Component {
  openAchievementModal() {
    console.log(this.props)
    this.props.openModal("test")
  }

  render() {
    return (
      <button onClick={function() { console.log(this.props); this.openAchievementModal() }} className="modal-button">Function bound - does not work - and should not</button>
    )
  }
}

class ArrowBoundExample extends React.Component {
  openAchievementModal() {
    console.log(this.props)
    this.props.openModal("test")
  }

  render() {
    return (
      <button onClick={() => { console.log(this.props); this.openAchievementModal() }} className="modal-button">Arrow bound - works</button>
    )
  }
}

class ES7ClassPropertyBoundExample extends React.Component {
  openAchievementModal = () => {
    console.log(this.props)
    this.props.openModal("test")
  }

  render() {
    return (
      <button onClick={() => { console.log(this.props); this.openAchievementModal() }} className="modal-button">ES7 class property bound - works</button>
    )
  }
}

const openModal = message => {
  alert(message)
}

class Examples extends React.Component {
  render() {
    return (
      <div>
        <ConstructorBoundExample openModal={openModal} />
        <InlineBoundExample openModal={openModal} />
        <FunctionBoundExample openModal={openModal} />
        <ArrowBoundExample openModal={openModal} />
        <ES7ClassPropertyBoundExample openModal={openModal} />
      </div>
    )
  }
}

ReactDOM.render(<Examples />, document.getElementById('app'));
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

如果您因某些原因不想使用bind,可以包装this.openAchievementModal功能。

<div className="cell">
     <button onClick={() => this.openAchievementModal()} className="modal-button">+ Add Achievement</button>
</div>

答案 2 :(得分:0)

我没有在任何地方看到showModal。据我了解,showModal必须发出一个动作。因此,您必须将第二个参数传递给connect函数:mapDispatchToProps

function mapDispatchToProps(dispatch) {
  return {
    showModal() {
      dispatch(
        type: 'SHOW_MODAL',
        modalType: 'DELETE_POST',
        modalProps: {
          postId: 42
        }
      )
    }   
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Achievement)

之后,您只需在函数this.props.showModal()

中调用openAchievementModal即可

编辑:我看到postId是硬编码的,您可能要考虑将值作为函数showModal的参数传递