如何使用 child 中的事件从 parent 组件调用函数?
How can I call a function from the parent component with an event in the child?
我正忙于学习 React,其中一项任务是构建一个每次尝试都会洗牌的游戏。卡片数组和洗牌方法在 parent 组件中(我使用 map 来渲染卡片),但重试按钮在 child 组件中。
我已经阅读了将近 20 个 Whosebug 问题和许多文档,但不知何故我的逻辑根本不起作用。
谁能帮我实现这个功能。
Parent:
import React from 'react'
import FlipCard from './FlipCard'
const cards = [
{
id: 1,
text: 'NOPE',
},
{
id: 2,
text: `!!WIN-
NER!!`,
},
{
id: 3,
text: 'NOPE',
},
]
const shuffle = array => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
const temp = array[i]
array[i] = array[j]
array[j] = temp
}
return array
}
const shuffleCards = shuffle(cards)
const CardGameUI = () => {
return (
<div className="cards-ui">
{cards.map(card => (
<FlipCard
key={card.id}
text={card.text}
value={card.id}
shuffleCards={shuffleCards}
/>
))}
</div>
)
}
export default CardGameUI
Child:
import React from 'react'
import ReactCardFlip from 'react-card-flip'
import FrontComponent from './FrontComponent'
import BackComponent from './BackComponent'
import RetryModal from './RetryModal'
class FlipCard extends React.Component {
constructor(props) {
super(props)
this.state = {
isFlipped: false,
gamesPlayed: 0,
gamesWon: 0,
gamesLost: 0,
show: false,
gameMsg: '',
}
this.handleFlip = this.handleFlip.bind(this)
this.handleShow = this.handleShow.bind(this)
this.handleClose = this.handleClose.bind(this)
this.handleReset = this.handleReset.bind(this)
this.handleRetry = this.handleRetry.bind(this)
}
handleReset() {
this.setState({
isFlipped: false,
gamesPlayed: 0,
gamesWon: 0,
gamesLost: 0,
show: false,
gameMsg: '',
})
this.props.shuffleCards
}
handleRetry() {
this.setState({
isFlipped: false,
show: false,
gameMsg: '',
})
this.props.shuffleCards
}
handleClose() {
this.setState({
show: false,
})
}
handleShow() {
this.setState({
show: true,
})
}
handleFlip() {
this.setState({
isFlipped: true,
gamesPlayed: +1,
show: false,
})
if (this.props.value !== 2) {
this.setState({
gamesLost: 0,
gameMsg:
'It looks like you lost this round. Want to play another?',
})
} else if (this.props.value === 2) {
this.setState({
gamesWon: 0,
gameMsg: 'CONGRATULATIONS!!!. Want to win again?',
})
}
setTimeout(() => {
this.handleShow()
}, 1000)
}
render() {
const text = this.props.text
const value = this.props.value
const isFlipped = this.state.isFlipped
return (
<>
<ReactCardFlip isFlipped={isFlipped} flipDirection="horizontal">
<FrontComponent onClick={this.handleFlip} />
<BackComponent text={text} value={value} />
</ReactCardFlip>
<RetryModal
handleShow={this.handleShow}
handleClose={this.handleClose}
handleReset={this.handleReset}
handleRetry={this.handleRetry}
show={this.state.show}
text={this.state.gameMsg}
/>
</>
)
}
}
export default FlipCard
我包含了这两个组件的所有代码,因为我不确定什么有助于澄清。
本质上,shuffleCards 方法需要在触发 handleReset 和 handleRetry 时 运行。
let array = [...cards];
const shuffle = () => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
const temp = array[i]
array[i] = array[j]
array[j] = temp
}
return array
}
只需用这段代码替换随机播放功能即可!
我希望它有效!
你基本上有 3 个问题:
您传递的 shuffleCards
道具实际上不是一个函数,而是调用洗牌函数的 结果。
您似乎通过在
子组件 - 而不是这里的 this.props.shuffleCards()
会导致错误,因为 this.props.shuffleCards
不是
功能。但这本身就是对问题的承认,因为
你显然需要在这里调用一个函数,以便做任何事情
发生了。
该函数本身实际上并没有创建一个新的随机数组,而是对旧数组进行了变异。是的,然后 returns,但是通过返回旧对象,即使是变异的形式,React 也看不到变化,因此不知道数组已经变异。
所以这是解决方法。首先,使卡片数组成为父函数中的状态变量,并使 shuffleCards
成为实际函数,存在于父组件中(因为它需要访问当前的 cards
状态),并且,而不是简单地改变旧的卡片数组,而是创建一个新数组并用它更新状态。
这是新的父组件,请注意您在其外部定义的 shuffle
和 shuffleCards
值不再需要或不需要:
const CardGameUI = () => {
const [shuffledCards, setShuffledCards] = React.useState(cards);
const shuffleCards = (currentCards) => {
const array = [...currentCards]; // this makes a (shallow) copy of the array, so it's recognised by React as something new
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
const temp = array[i]
array[i] = array[j]
array[j] = temp
}
setShuffledCards(array);
};
return (
<div className="cards-ui">
{shuffledCards.map(card => (
<FlipCard
key={card.id}
text={card.text}
value={card.id}
shuffleCards={shuffleCards}
/>
))}
</div>
)
}
然后您需要更新子组件,以便它实际调用此 state-updating 函数,方法是按照我在上面第 2) 点中指出的进行更改:将 this.props.shuffleCards
替换为 this.props.shuffleCards()
.
当在子组件中采取适当的操作时,卡片现在应该可以正确洗牌。 (让我知道是否还有什么不能按预期工作 - 我还没有实际测试过。)
我正忙于学习 React,其中一项任务是构建一个每次尝试都会洗牌的游戏。卡片数组和洗牌方法在 parent 组件中(我使用 map 来渲染卡片),但重试按钮在 child 组件中。
我已经阅读了将近 20 个 Whosebug 问题和许多文档,但不知何故我的逻辑根本不起作用。
谁能帮我实现这个功能。
Parent:
import React from 'react'
import FlipCard from './FlipCard'
const cards = [
{
id: 1,
text: 'NOPE',
},
{
id: 2,
text: `!!WIN-
NER!!`,
},
{
id: 3,
text: 'NOPE',
},
]
const shuffle = array => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
const temp = array[i]
array[i] = array[j]
array[j] = temp
}
return array
}
const shuffleCards = shuffle(cards)
const CardGameUI = () => {
return (
<div className="cards-ui">
{cards.map(card => (
<FlipCard
key={card.id}
text={card.text}
value={card.id}
shuffleCards={shuffleCards}
/>
))}
</div>
)
}
export default CardGameUI
Child:
import React from 'react'
import ReactCardFlip from 'react-card-flip'
import FrontComponent from './FrontComponent'
import BackComponent from './BackComponent'
import RetryModal from './RetryModal'
class FlipCard extends React.Component {
constructor(props) {
super(props)
this.state = {
isFlipped: false,
gamesPlayed: 0,
gamesWon: 0,
gamesLost: 0,
show: false,
gameMsg: '',
}
this.handleFlip = this.handleFlip.bind(this)
this.handleShow = this.handleShow.bind(this)
this.handleClose = this.handleClose.bind(this)
this.handleReset = this.handleReset.bind(this)
this.handleRetry = this.handleRetry.bind(this)
}
handleReset() {
this.setState({
isFlipped: false,
gamesPlayed: 0,
gamesWon: 0,
gamesLost: 0,
show: false,
gameMsg: '',
})
this.props.shuffleCards
}
handleRetry() {
this.setState({
isFlipped: false,
show: false,
gameMsg: '',
})
this.props.shuffleCards
}
handleClose() {
this.setState({
show: false,
})
}
handleShow() {
this.setState({
show: true,
})
}
handleFlip() {
this.setState({
isFlipped: true,
gamesPlayed: +1,
show: false,
})
if (this.props.value !== 2) {
this.setState({
gamesLost: 0,
gameMsg:
'It looks like you lost this round. Want to play another?',
})
} else if (this.props.value === 2) {
this.setState({
gamesWon: 0,
gameMsg: 'CONGRATULATIONS!!!. Want to win again?',
})
}
setTimeout(() => {
this.handleShow()
}, 1000)
}
render() {
const text = this.props.text
const value = this.props.value
const isFlipped = this.state.isFlipped
return (
<>
<ReactCardFlip isFlipped={isFlipped} flipDirection="horizontal">
<FrontComponent onClick={this.handleFlip} />
<BackComponent text={text} value={value} />
</ReactCardFlip>
<RetryModal
handleShow={this.handleShow}
handleClose={this.handleClose}
handleReset={this.handleReset}
handleRetry={this.handleRetry}
show={this.state.show}
text={this.state.gameMsg}
/>
</>
)
}
}
export default FlipCard
我包含了这两个组件的所有代码,因为我不确定什么有助于澄清。
本质上,shuffleCards 方法需要在触发 handleReset 和 handleRetry 时 运行。
let array = [...cards];
const shuffle = () => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
const temp = array[i]
array[i] = array[j]
array[j] = temp
}
return array
}
只需用这段代码替换随机播放功能即可! 我希望它有效!
你基本上有 3 个问题:
您传递的
shuffleCards
道具实际上不是一个函数,而是调用洗牌函数的 结果。您似乎通过在 子组件 - 而不是这里的
this.props.shuffleCards()
会导致错误,因为this.props.shuffleCards
不是 功能。但这本身就是对问题的承认,因为 你显然需要在这里调用一个函数,以便做任何事情 发生了。该函数本身实际上并没有创建一个新的随机数组,而是对旧数组进行了变异。是的,然后 returns,但是通过返回旧对象,即使是变异的形式,React 也看不到变化,因此不知道数组已经变异。
所以这是解决方法。首先,使卡片数组成为父函数中的状态变量,并使 shuffleCards
成为实际函数,存在于父组件中(因为它需要访问当前的 cards
状态),并且,而不是简单地改变旧的卡片数组,而是创建一个新数组并用它更新状态。
这是新的父组件,请注意您在其外部定义的 shuffle
和 shuffleCards
值不再需要或不需要:
const CardGameUI = () => {
const [shuffledCards, setShuffledCards] = React.useState(cards);
const shuffleCards = (currentCards) => {
const array = [...currentCards]; // this makes a (shallow) copy of the array, so it's recognised by React as something new
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
const temp = array[i]
array[i] = array[j]
array[j] = temp
}
setShuffledCards(array);
};
return (
<div className="cards-ui">
{shuffledCards.map(card => (
<FlipCard
key={card.id}
text={card.text}
value={card.id}
shuffleCards={shuffleCards}
/>
))}
</div>
)
}
然后您需要更新子组件,以便它实际调用此 state-updating 函数,方法是按照我在上面第 2) 点中指出的进行更改:将 this.props.shuffleCards
替换为 this.props.shuffleCards()
.
当在子组件中采取适当的操作时,卡片现在应该可以正确洗牌。 (让我知道是否还有什么不能按预期工作 - 我还没有实际测试过。)