Как передать свойство React Component дочерней функции с помощью {children}? - PullRequest
2 голосов
/ 05 апреля 2020

Я новичок в React, так что это может быть чем-то очевидным, но я не могу пройти через подпорку функции, которая создает Компоненты из своего родителя.

Я могу передавать реквизиты дочерним компонентам, но то же самое не работает для функций.

У меня есть <Subscription>, который я могу передать через аргументы, например, от его родителя post :

<Subscription auth={auth} stripeAmount={post.amount} stripePlanId={post.planid}/>

Это создает подписку Stripe. Я хочу ограничить подписку подпиской на stripePlanId, которую я делаю через:

class Subscription extends React.Component {

  // https://stripe.com/docs/checkout#integration-custom
  componentDidMount() {
    this.stripeCheckout = window.StripeCheckout.configure({
      ...etc...
      email: this.props.auth.email,
    })
  }

  newSubscription = () => {
    var stripePlanId = this.props.stripePlanId;
    this.stripeCheckout.open({
      amount: this.props.stripeAmount, // in cents
      description: this.props.stripePlanId,
      token: function(token){
        createSubscription(token, stripePlanId)
      }
    })
  }

 ..etc..

Это прекрасно работает. Но теперь, чтобы пройти через stripePlanId, я не могу выяснить, как пропустить stripePlanId, поскольку он выполняет рендеринг через функцию - этот аргумент {children}, кажется, только передает функцию, а попытка добавить аргументы вызывает ошибки, которые они не являются функциями, которые, как он ожидает, будут действовать на основе переданных аргументов:

const FireflySubscription = ({children}) => (
  <FirebaseAuth>
    { ({isLoading, error, auth}) => {
      if (error || isLoading || !auth) {
        //it pushes these arguments to the parent function
        return children({ 
          error,
          isLoading,
          subscription: null,
        })
      }

      // the issue - how to populate this?
      const stripePlanId = ""  

      // when working this should return the subscription for only this planId
      if (stripePlanId) {
        return <FirestoreCollection
        path="subscriptions"
        filter={[['createdBy', '==', auth.uid], ['stripePlanId','==', stripePlanId]]}
      >
        { ({error, isLoading, data}) => {
          return children({
            error,
            isLoading,
            subscription: data.length > 0 ? data : null,
          })
        }}
      </FirestoreCollection>

      }

      return <FirestoreCollection
        path="subscriptions"
        filter={['createdBy', '==', auth.uid]}
      >
        { ({error, isLoading, data}) => {
          return children({
            error,
            isLoading,
            subscription: data.length > 0 ? data : null,
          })
        }}
      </FirestoreCollection>

    }}
  </FirebaseAuth>
)

export default FireflySubscription

Я пытался передать его другим методом, но "scope" не проходит:

getPostSubscriptions = stripePlanId => {
    return <FireflySubscription>
// it gets these arguments from FireflySubscription function above
    { ({error, isLoading, subscription}) => { 
      if (error) {
        return <Error error={error} />
      }

      if (isLoading) {
        return <p>loading...</p>
      }

      if (!subscription) {
        return <div>
          <p><strong>Subscribe to get paid features</strong></p>
          ..etc...
        </div>
      }

      ..etc...

    }}
  </FireflySubscription>
  }

  render() {
    return this.getPostSubscriptions(this.props.stripePlanId)
  }
}

Любая подсказка наиболее ценится! Исходный код, который я адаптирую, взят из https://github.com/sampl/firefly, если это поможет.

Ответы [ 2 ]

3 голосов
/ 20 апреля 2020

Если исходить из репозитория, на который вы ссылаетесь, кажется, что вы визуализируете FireflySubscription из компонента подписки, например

class Subscription extends React.Component {
    // other code here

    render() {
       return (
           <FireflySubscription>
               { ({error, isLoading, subscription}) => {
                   /*Some components here*/
               }}
           </FireflySubscription>
       )
    }
}

Учитывая вышесказанное, самое простое решение для вас - передать stripePlanId в качестве опоры для FireflySubscription компонента и получения его внутри компонента вместе с дочерними элементами

Теперь, когда stripePlanId вычислено внутри Subscription компонента, его можно легко передать дочерним элементам FireflySubscription напрямую от родителя, не беспокоясь о его маршрутизации через FireflySubscription

Таким образом, решение будет выглядеть так:

class Subscription extends React.Component {
    // other code here

    render() {
       return (
           <FireflySubscription stripePlanId={this.props.stripePlanId}>
               { ({error, isLoading, subscription}) => {
                   // stripePlanId can be passed on to any children here using this.props.stripePlanId directly
                   /*Some components here*/
               }}
           </FireflySubscription>
       )
    }
}

Теперь в FireflySubscription вы будете использовать его как

const FireflySubscription = ({children, stripePlanId}) => (
  <FirebaseAuth>
    { ({isLoading, error, auth}) => {
      if (error || isLoading || !auth) {
        //it pushes these arguments to the parent function
        return children({ 
          error,
          isLoading,
          subscription: null,
        })
      }

      if (stripePlanId) {
        return <FirestoreCollection
        path="subscriptions"
        filter={[['createdBy', '==', auth.uid], ['stripePlanId','==', stripePlanId]]}
      >
        { ({error, isLoading, data}) => {
          return children({
            error,
            isLoading,
            subscription: data.length > 0 ? data : null,
          })
        }}
      </FirestoreCollection>

      }

      return <FirestoreCollection
        path="subscriptions"
        filter={['createdBy', '==', auth.uid]}
      >
        { ({error, isLoading, data}) => {
          return children({
            error,
            isLoading,
            subscription: data.length > 0 ? data : null,
          })
        }}
      </FirestoreCollection>

    }}
  </FirebaseAuth>
)
2 голосов
/ 20 апреля 2020

Использование Render Props

Термин «render prop» относится к методике совместного использования кода между компонентами React с использованием реквизита, значение которого является функцией.

Компонент с реквизитом рендеринга берет функцию, которая возвращает элемент React, и вызывает его вместо реализации собственной логики рендеринга c.

Компонент ParentPost:

const ParentPost = () => {
    <Subscription auth={auth} stripeAmount={post.amount} stripePlanId={post.planid}>
        {(stripePlanId) => <FireflySubscription stripePlanId={stripePlanId}/>}
    </Subscription>
};

Компонент подписки: В методе рендеринга передайте stripePlanId в качестве опоры для children

class Subscription extends React.Component {
  // https://stripe.com/docs/checkout#integration-custom
  componentDidMount() {
    this.stripeCheckout = window.StripeCheckout.configure({
      // ...etc...
      email: this.props.auth.email
    });
  }

  newSubscription = () => {
    var stripePlanId = this.props.stripePlanId;
    this.stripeCheckout.open({
      amount: this.props.stripeAmount, // in cents
      description: this.props.stripePlanId,
      token: function(token) {
        createSubscription(token, stripePlanId);
      }
    });
  };

  render() {
      <div>
          ...
          {this.props.children(this.props.stripePlanId)}
          ...
      </div>
  }
}

Компонент подписки Firefly: Вот, получите stripePlanId от родителя вот так:.

const FireflySubscription = ({children, stripePlanId}) => (
    <FirebaseAuth>
        {({isLoading, error, auth}) => {
            if (error || isLoading || !auth) {
                //it pushes these arguments to the parent function
                return children({
                    error,
                    isLoading,
                    subscription: null,
                })
            }


            //const stripePlanId = stripePlanIdFromParent; // dont need this as we are destructuring from props

            // when working this should return the subscription for only this planId
            if (stripePlanId) {
            ...
...