简体   繁体   中英

What's the alternative to public methods when you switch from class-based to function-based React components?

React components that use hooks can't be class-based; they have to be function-based. There's something I used to do in class-based components that I'm not sure how to accomplish in function-based components: calling a method on a child component.

The context where this comes up for me is in components that act like a map, or like a photoshop document. They often have a way of zooming to a specific area, while also allowing the user to freely pan around afterward.

One might design such a <Zoomable> component to keep internal state about where it's zoomed to, but provide a public method zoomTo(place) .

One could use the Zoomable component like this:

const ZoomableUI = ({place}) => {
    const zoomable = React.useRef(null)

    React.useEffect(() => {
        zoomable.zoomTo(place)
    }, [place])

    return (
        <div>
            <Zoomable ref={zoomable} />
            <button onClick={() => zoomable.current.zoomTo(place)}>
                Re-Center
            </button>
        </div>
    )
}

This component causes the Zoomable to zoom to a place when it first renders, or when the place property changes, but the user could freely pan around theirself afterward. They could then re-center it on the current place by clicking the button.

Of course, none of this would work if the Zoomable was implemented as a function-based component. How would you implement this sort of system with a function-based map?

What's the alternative to providing public methods when you switch to function-based React components?

Btw, don't suggest that I just put the button inside the Zoomable. Its purpose is to serve as an example of behavior that could come from anywhere in the app, not necessarily somewhere co-located in the DOM.

You could turn Zoomable into a fully controlled component. Then the parent component would need to manage its zoom level and position.

const ZoomableUI = ({place}) => {
    const [mapState, setMapState] = React.useState(place)

    React.useEffect(() => {
        setMapState(place)
    }, [place])

    return (
        <div>
            <Zoomable state={mapState} setState={setMapState} />
            <button onClick={() => setMapState(place)}>
                Re-Center
            </button>
        </div>
    )
}

Another possibility I see is to pass the place in as a prop. Zoomable could have an internal useEffect that would zoom it to the place when the place changed. This wouldn't work for the button though, since the button doesn't change the prop. I could put a counter in the Zoomable's key prop to cause it to refresh its state.

const ZoomableUI = ({place}) => {
    const [counter, setCounter] = React.useState(0)
    return (
        <div>
            <Zoomable place={place} key={counter} />
            <button onClick={() => setCounter((counter) => counter + 1)}>
                Re-Center
            </button>
        </div>
    )
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM