0

I want to create a react component instance and render it in a static place programmatically. My use-case is that I open a sequence of dialogs in an unknown length and when I get a response from a dialog I open the next.

I want to do something like:

const DialogExample = () => ({ question, onAnswer }) =>
  (<div>
    {question}
    <button onClick={onAnswer}>answer</button>
  </div>);

class SomeComponent extends React.Component {
  async start() {
    const questions = await getSomeDynamicQuestions();
    this.ask(questions);
  }

  ask(questions) {
    if (questions.length === 0) {
      // DONE.. (do something here)
      return;
    }

    const current = questions.pop();

    React.magicMethod(
        // The component I want to append:
        <DialogExample 
            question={current} 
            onAnswer={() => this.ask(questions)}
        />,
        // Where I want to append it:
        document.getElementsByTagName('body')[0]);
  }

  render() {
    return (
      <div>
        <button onClick={this.start}>start</button>
      </div>);
  }
}

I know that's not very "react-like", and I guess the "right" way of doing it will be storing those questions in state and iterate over them in "someComponent" (or other) render function, but still, I think that this pattern can make sense in my specific need.

1 Answer 1

2

Sounds like a case for Portals. I'd recommend doing something like this:

class SomeComponent extends React.Component {
  constructor(props) {
    super(props);
    this.body = document.getElementsByTagName('body')[0];
    this.state = {
      questions: [],
    }
  }

  async start() {
    const questions = await getSomeDynamicQuestions();
    this.setState({ questions });
  }

  nextQuestion() {
    this.setState(oldState => {
      const [first, ...rest] = oldState.questions;
      return { questions: rest };
    })
  }

  render() {
    const { questions } = this.state;
    return (
      <div>
        <button onClick={this.start}>start</button>
        {questions.length > 0 && ReactDOM.createPortal(
          <DialogExample
            question={questions[0]}
            onAnswer={() => this.nextQuestion()}
          />,
          this.body,
        )}
      </div>
    );
  }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Not exactly my Intention, wanted to know if I can somehow "create a portal outside the render function", something which is more similar to my original code. Anyway, I'm pretty sure it's impossible so I solved it using portals.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.