Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I have multiple layers of React components for getting an embed from a music service API, including a higher-order component that hits the API to populate the embed. My problem is that my lowest-level child component won't change state. I basically want the populated embed (lowest level component) to display an album cover, which disappears after clicking it (revealing an iframe), and whose state remains stable barring any change in props higher up (by the time this component is revealed, there should be no other state changes aside from focus higher up). Here's the code:

Parent:

return (
            /*...*/
                <Embed
                    embed={this.props.attributes.embed}
                    cb={updateEmbed}
                />
            /*...*/

First child ( above):

render() {
        const {embed, className, cb} = this.props;
        const {error, errorType} = this.state;
        const WithAPIEmbed = withAPI( Embed );

        /*...*/

        return <WithAPIEmbed
            embed={embed[0]}
            className={className}
            cb={cb}
        />;
        /*...*/

withAPI:

/*...*/
        componentWillMount() {
            this.setState( {fetching: true} );
        }

        componentDidMount() {
            const {embed} = this.props;

            if ( ! embed.loaded ) {
                this.fetchData();
            } else {
                this.setState( {
                    fetching: false,
                    error: false,
                } );
            }
        }

        fetchData() {
           /*... some API stuff, which calls the callback in the top level parent (cb()) setting the embed prop when the promise resolves -- this works just fine ...*/ 
        }

        render() {
            const {embed, className} = this.props;
            const {fetching, error, errorType} = this.state;

            if ( fetching ) {
                /* Return some spinner/placeholder stuff */
            }

            if ( error ) {
                /* Return some error stuff */
            }

            return (
                <WrappedComponent
                    {...this.props}
                    embed={embed}
                />
            )
        }

And finally the last child I'm interested in:

constructor() {
        super( ...arguments );
        this.state = {
            showCover: true,
        };
    }

    render() {
        const {embed, setFocus, className} = this.props;
        const {showCover} = this.state;

        if ( showCover ) {
            return [
                <div key="cover-image" className={classnames( className )}>
                    <figure className='cover-art'>
                        <img src={embed.coverArt} alt={__( 'Embed cover image' )}/>
                        <i onClick={() => {
                            this.setState( {showCover: false,} );
                        }}>{icon}</i> // <-- Play icon referenced below.
                    </figure>
                </div>,
            ]
        }

        return [
            <div key="embed" className={className}>
                    <EmbedSandbox
                        html={iframeHtml}
                        type={embed.embedType}
                        onFocus={() => setFocus()}
                    />
            </div>,
        ];
    }

My issue is that clicking the play icon should clear the album cover and reveal the iframe embed, but even though the click is registering, the state never changes (or does and then changes back). I believe it's because a higher-level component is mounting/unmounting and reinstantiating this component with its default state. I could move this state up the tree or use something like Flux, but I really feel I shouldn't need to do that, and that there's something fundamental I'm missing here.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
199 views
Welcome To Ask or Share your Answers For Others

1 Answer

The problem is that const WithAPIEmbed = withAPI( Embed ); is inside the render method. This creates a fresh WithAPIEmbed object on each render, which will be remounted, clearing any state below. Lifting it out of the class definition makes it stable and fixes the problem.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...