如何使用 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 个问题:

  1. 您传递的 shuffleCards 道具实际上不是一个函数,而是调用洗牌函数的 结果

  2. 您似乎通过在 子组件 - 而不是这里的 this.props.shuffleCards() 会导致错误,因为 this.props.shuffleCards 不是 功能。但这本身就是对问题的承认,因为 你显然需要在这里调用一个函数,以便做任何事情 发生了。

  3. 该函数本身实际上并没有创建一个新的随机数组,而是对旧数组进行了变异。是的,然后 returns,但是通过返回旧对象,即使是变异的形式,React 也看不到变化,因此不知道数组已经变异。

所以这是解决方法。首先,使卡片数组成为父函数中的状态变量,并使 shuffleCards 成为实际函数,存在于父组件中(因为它需要访问当前的 cards 状态),并且,而不是简单地改变旧的卡片数组,而是创建一个新数组并用它更新状态。

这是新的父组件,请注意您在其外部定义的 shuffleshuffleCards 值不再需要或不需要:

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() .

当在子组件中采取适当的操作时,卡片现在应该可以正确洗牌。 (让我知道是否还有什么不能按预期工作 - 我还没有实际测试过。)