简体   繁体   中英

How can I allow only a single instance of a React component to be present?

I am trying to build a functionality in react.

The problem goes like this: I have three components ComponentA, ComponentB, ComponentC. ComponentC can be rendered as a child of either ComponentA or ComponentB.

What I want to achieve is that at any point of time there is only one instance of ComponentC present in the DOM, can be either as a child of ComponentA or ComponentB, both of which are always present.

class ComponentA extends Component {
  render() {
    return (
      <ComponentC />
    );
  }
}

class ComponentB extends Component {
  render() {
    return (
      <ComponentC />
    );
  }
}

class ComponentC extends Component {
  render() {
    return (
      <div>This is ComponentC </div>
    );
  }
}

I tried to build a way to unmount any instance of ComponentC (if present) whenever it is mounted but couldn't succeed.

There are a few ways you can do this. The simplest method that comes to mind is simply to check the DOM if said node exists. If it exists, just render nothing or prevent the state from toggling it.

 class MyApp extends React.Component { constructor() { super(); this.state = { showA: false, showB: false }; } toggleA = () => { if(document.getElementById("component_c") && !this.state.showA) return; this.setState((prevState) => ({showA: !prevState.showA})); } toggleB = () => { if(document.getElementById("component_c") && !this.state.showB) return; this.setState((prevState) => ({showB: !prevState.showB})); } render() { return( <div> <div> <button onClick={this.toggleA}>Show/Hide A</button> <button onClick={this.toggleB}>Show/Hide B</button> </div> {this.state.showA && <ComponentA />} {this.state.showB && <ComponentB />} </div> ); } } class ComponentA extends React.Component { render() { return ( <ComponentC parent="A" /> ); } } class ComponentB extends React.Component { render() { return ( <ComponentC parent="B" /> ); } } class ComponentC extends React.Component { render() { return ( <div id="component_c">{"This is ComponentC, rendered from component" + this.props.parent}</div> ); } } ReactDOM.render(<MyApp />, document.getElementById("app")); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="app"></div> 

Implementing a reference counter with hooks does the job:

 function Highlander(Component) { let immortals = 0 return function(props) { const [onlyOne, thereIsOnlyOne] = React.useState(false) React.useEffect(() => { immortals++ if (immortals === 1) { thereIsOnlyOne(true) } () => { immortals-- } }, []) return onlyOne ? <Component { ...props}/> : <React.Fragment></React.Fragment> } } function Test() { return "test" } const OnlyOneTest = Highlander(Test) ReactDOM.render(<React.Fragment> <OnlyOneTest /> <OnlyOneTest /> <OnlyOneTest /> </React.Fragment>, document.getElementById("react"));
 <div id="react"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>

Starring Highlander as a HoC, which "singletons" the composed component (get the Highlander reference ? uhuh).

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