开发者问题收集

在 ReactJS 中通过父状态的 props 传递函数

2018-01-16
725

这可能有点令人困惑,所以我会尽量简化它。

我的状态包含父组件 App.js 中的对象数组。这是一个对象的示例。

{
category: "sample category",
API: "API URL",
active: function()
}

在我的子组件 Category.js 中,我通过 prop 传递了该数组。子组件渲染 ul ,然后映射到数组上以为每个类别创建一个 li 。每个列表项都应该有一个 onClick 事件,该事件触发对象中关联的 active: function()

由于某种原因,这不起作用。如果我运行测试,其中函数是一个简单的 console.log("success") ,那就没问题了。因此,列表项正在选择该函数并通过我的 props 正常运行它。

这让我相信问题出在我正在运行的实际函数上。我尝试运行的函数是这个(位于 App.js 中)

function() {
  this.setState({categoryActive: "https://opentdb.com/api.php?amount=20&category=11"});
  this.setState({showQuestions: true});
}

正在设置的那些状态位于 App.js 中,并且该函数也位于 App.js 中,但正在通过 Category.js 中的列表项的 prop 运行。这是我的问题吗?我无法通过在子组件中运行函数来调整 App.js 的状态吗?


编辑:这里有更多代码可帮助您找出解决方案。

实际错误:Uncaught TypeError:无法读取 catMovies (App.js:71) 中未定义的属性“setState”

App.js:71 是我的 catMovies() 函数的 this.setState({categoryActive: "https://opentdb.com/api.php?amount=20&category=11"});

我的 App.js 构造函数

constructor(props) {
super(props);
this.state = {
  showCategories: false,
  showQuestions: false,
  isQuestionLoaded: false,
  categories: [{
    category: "movies",
    API: "https://opentdb.com/api.php?amount=10&category=11",
    active: this.catMovies
  }, {
    category: "sports",
    API: "https://opentdb.com/api.php?amount=10&category=21",
    active: this.catSports
  }, {
    category: "books",
    API: "https://opentdb.com/api.php?amount=10&category=10",
    active: this.catBooks
  }, {
    category: "videogames",
    API: "https://opentdb.com/api.php?amount=10&category=15",
    active: this.catVideogames
  }, {
    category: "general",
    API: "https://opentdb.com/api.php?amount=10&category=9",
    active: this.catGeneral
  }, {
    category: "everything",
    API: "https://opentdb.com/api.php?amount=10",
    active: this.catEverything
  }],
  categoryActive: "",
  questionNumber: 0,
  questionTotal: null,
  correct: [],
  score: 0,
  showFinal: false,
  testState: "test"
};
this.showCategories = this.showCategories.bind(this);
this.showQuestions = this.showQuestions.bind(this);
this.catMovies = this.catMovies.bind(this);
}

这是 App.js 中的实际函数:

catMovies() {
  this.setState({categoryActive: "https://opentdb.com/api.php?amount=20&category=11"});
  this.setState({showQuestions: true});
}

这是调用子组件的 App.js 的返回值:

<Categories categoryList={ this.state.categories } onClick={ this.catMovies } />

最后,这是子组件中使用所有这些的映射:

const categoryListItems = this.props.categoryList.map(cat =>
  <a href="#questionsContainer" key={cat.category + "link"}>
    <li onClick={cat.active} key={cat.category}>
      {cat.category}
    </li>
  </a>
);
2个回答

您的问题在于处理程序的绑定。
您不应该像在构造函数中那样覆盖处理程序,而应该直接在状态初始化中执行此操作:

this.state = {
  showCategories: false,
  showQuestions: false,
  isQuestionLoaded: false,
  categories: [{
    category: "movies",
    API: "https://opentdb.com/api.php?amount=10&category=11",
    active: this.catMovies.bind(this) // <-- you need to bind here
  },
 //...

这是一个包含两种模式的运行示例,请注意 turnOff 处理程序将如何抛出与您收到的相同的错误:

const LightBall = ({ on }) => {
  return (
    <div>{`The light is ${on ? 'On' : 'Off'}`}</div>
  );
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      lightOn: false,
      toggle: this.toggleLight.bind(this), // this will work
      turnOff: this.turnOff // this will not work
    };
    this.turnOff = this.turnOff.bind(this);
  }

  toggleLight(){this.setState({ lightOn: !this.state.lightOn })};

  turnOff() { this.setState({ lightOn: false }) };

  render() {
    const { lightOn, toggle, turnOff } = this.state;
    return (
      <div>
        <button onClick={toggle}>Toggle Light</button>
        <button onClick={turnOff}>Turn Off</button>
        <LightBall on={lightOn} />
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<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="root"></div>
Sagiv b.g
2018-01-16

该问题可能是由于此函数中的

function() {
  this.setState({categoryActive: "https://opentdb.com/api.php?amount=20&category=11"});
  this.setState({showQuestions: true});
}

this 指向调用该函数的类。

您可以通过使用 bind() 将函数绑定到 App 组件来解决此问题,然后再将数组传递给子组件(更多信息请参见此处 https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

或者,如果您在 App 组件中定义箭头函数,则可以使用箭头函数将其范围自动绑定到该组件,因此

{
category: "sample category",
API: "API URL",
active: () => {
    this.setState({categoryActive: "https://opentdb.com/api.php?amount=20&category=11"});
    this.setState({showQuestions: true});
  }
}

有关箭头函数的更多信息,请参阅此处 https://babeljs.io/learn-es2015/#arrows-and-lexical-this

Carlo
2018-01-16