How to update state of all childs rendered with map() inside a parent?

120 Views Asked by At

my goal is to time the visibility of child components based on the playback time of a video. This components, called InfoElement, are rendered thanks to map method.

{this.props.sceneNavigator.viroAppProps.tours[this.state.tourId].scenes[this.state.sceneId].infoElements.map((infoElement, key) => {
return (
    <InfoElement 
        content={{uri: infoElement.poiImage}}
        startingTime = {infoElement.startingTime}
        endingTime = {infoElement.endindTime}
        contentCardScale={[5, 5, 5]} 
        position={polarToCartesian(infoElement.coordinate)}/>
    )
    })}

Inside the parent, there is a callback, _onUpdateTime(currentPlaybackTimeInSeconds, totalPlayBackDurationInSeconds) that is called when the current playback position of the video has changed.

<Viro360Video
    source={{uri:this.props.sceneNavigator.viroAppProps.tours[this.state.tourId].scenes[this.state.sceneId].backgroundSource}}
    onBufferEnd={this.onLoadEnded}
    onUpdateTime={this._onUpdateTime}
    rotation={[0, 0, 0]}
    onError={this.onError}/>

    _onUpdateTime(currentPlaybackTimeInSeconds, totalPlayBackDurationInSeconds) {
  this.setState({
                  visible : false,
               });

           console.log(currentPlaybackTimeInSeconds);
        }

What i want to do is to compare the currentPlaybackTimeInSeconds with startingTime and endingTime and make the infoElement component visible only ifcurrentPlaybackTimeInSeconds is between startingTime and endingTime. My problem is that i don't have just one infoElement, but a list, so i don't know how to refer to each of them. The idea would be to update the state of each of them, but i have no idea how to do that. Any advice? Thanks to all that will answer me.

1

There are 1 best solutions below

0
Kyle On

You need to inverse your thinking on this one. What you should be doing is telling each InfoElement what the current time is, and then they change their rendered output based on that time. Something like this:

const React = require('react');
const {useState,useCallback} = React;

const InfoElement = ({currentTime,startingTime,endingTime}) => {
    if(currentTime >= startingTime && currentTime < endingTime){
        return (
            <>
                {/* Your render code here*/}
            </>
        );
    }
    else {
        return null;
    }
}

const App = ({sceneNavigator}) => {
    const [currentTime,setCurrentTime] = useState(-1);
    const [tourId,setTourId] = useState(-1);
    const [sceneId,setSceneId] = useState(-1);

    const onUpdateTime = useCallback((time) => {
        setCurrentTime(time);
    },[]);

    let infoElements = [];
    if(sceneNavigator.viroAppProps.tours[tourId] != null && sceneNavigator.viroAppProps.tours[tourId].scenes[sceneId] != null){
        infoElements = sceneNavigator.viroAppProps.tours[tourId].scenes[sceneId].infoElements;
    }

    return (<>
        <Viro360Video
            onUpdateTime={onUpdateTime}

        />

        {infoElements.map((infoElement, key) => (
            <InfoElement
                key={`infoElement-${key}`}
                content={{uri: infoElement.poiImage}}
                startingTime = {infoElement.startingTime}
                currentVideoTime={currentTime}
                endingTime = {infoElement.endindTime}
                contentCardScale={[5, 5, 5]} 
                position={polarToCartesian(infoElement.coordinate)}/>
            )
        )}
    </>);
}

In this, you can see that we're hooking to the Viro360Video onUpdateTime and setting a currentTime state. We then pass the currentTime to each InfoElement. Inside the InfoElement we check the currentTime passed in against the startingTime and endingTime, if we're inside those bounds, we render our elements, otherwise, we return null.

Notes

  1. I'm using hooks inside functional components instead of using react classes. See their usage here: https://reactjs.org/docs/hooks-intro.html
  2. I used Fragments (<></>) instead of a div or a View since I don't know if you're targeting React Native or ReactDOM or some other rendering engine.