Reputation: 2631
I'm new to React so this may be something obvious but I can't pass through a prop to a function that creates Components from its parent.
I can pass props to child components, but the same doesn't work for functions.
I have <Subscription>
which I can pass through arguments like so, from its parent post
:
<Subscription auth={auth} stripeAmount={post.amount} stripePlanId={post.planid}/>
This creates a Stripe subscription. I want to limit the subscription to subscribe to the stripePlanId
which I do so via:
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..
This works great. But now to pass through the stripePlanId I can't find out how to pass the stripePlanId
through since it renders via a function - this {children}
argument seems to only pass in the function, and attempting to add arguments causes errors that they are not functions it expects to act upon the arguments passed:
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
I have tried to pass it through with another method, but the "scope" does not pass through:
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)
}
}
Any clue most appreciated! The original code I'm adapting is from https://github.com/sampl/firefly if that helps.
Upvotes: 2
Views: 612
Reputation: 12222
Going by the repository that you refer, its seems like you are rendering FireflySubscription
from within Subscription component like
class Subscription extends React.Component {
// other code here
render() {
return (
<FireflySubscription>
{ ({error, isLoading, subscription}) => {
/*Some components here*/
}}
</FireflySubscription>
)
}
}
Considering the above, the simplest solution for you is to pass on the stripePlanId
as a prop to FireflySubscription
component and receive it inside the component along with children
Now that stripePlanId
is calculated inside Subscription
component, it can easily be passed to the children of FireflySubscription
directly from parent without worrying about it being routed through FireflySubscription
So the solution would look like
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>
)
}
}
Now in FireflySubscription, you will use it as
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>
)
Upvotes: 2
Reputation: 13682
Use Render Props
The term “render prop” refers to a technique for sharing code between React components using a prop whose value is a function.
A component with a render prop takes a function that returns a React element and calls it instead of implementing its own render logic.
ParentPost Component:
const ParentPost = () => {
<Subscription auth={auth} stripeAmount={post.amount} stripePlanId={post.planid}>
{(stripePlanId) => <FireflySubscription stripePlanId={stripePlanId}/>}
</Subscription>
};
Subscription Component: In your render method, pass stripePlanId
as prop to the 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>
}
}
FireflySubscription Component: In here, receive the stripePlanId
from the parent like this:.
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) {
...
Upvotes: 2