Closed Huanzhang89 closed 5 years ago
@Huanzhang89 Have you considered abstracting the Page
into its own component and then passing state from the parent as props, using a callback for updating props to the parent, etc..?
@njj I did consider that but the issue here is passing the props down to Wizard.Page and accessing them inside Wizard.Page. Even if we abstract the Page
into its own component, at the point where we call <Page props={parentState} />
we still do not have access to the parentState
to pass into the Page
component.
I have however found a solution to this using renderProps! The problem is that inside the static method Wizard.Page, the props are not passed down to the children as it simply returns the children.
static Page = ({ children }) => {
return children
}
By modifying this static method to
static Page = ({ children, parentState }) => {
return children(parentState)
}
And also modifying the JSX inside each Wizard.Page to be a function that returns the JSX, we can pass the parentState down to each Wizard.Page, like so.
<Wizard.Page>
{props => {
return (
// JSX here
)}
}
</Wizard.Page>
props in the above function = parentState
Hope this helps anyone else getting stuck with this issue!
@Huanzhang89 Nice, you could also do this by using React.cloneElement
for the {activePage}
, i.e:
{ React.cloneElement(activePage, { ...props })
Oh really, do you mean instead of just {activePage} inside the render function of the Wizard?
But if you look at the first post I made, I am already using React.cloneElement to pass the parentState
to the activePage
.
I think we still need to renderProps inside Wizard.Page to pass the parentState
down to the children. Not 100% sure about this though.
@Huanzhang89 Right then you will need to handle it in the Page function again. Either way I think works. I ended up doing something similar, but I'm passing the Formik props (values, fns, etc..) so my fields can have them.
Ah ok I see what you mean. Maybe we should create a PR to either update the current example or create a new multipage example? I can see a lot of use cases where we would need to access the form data inside each page and its not immediately clear how this can be done from the current example.
@njj Actually after some testing I found that my method doesn't update the form values correctly as the React.cloneElement
was outside of the Formik
component. So your method of { React.cloneElement(activePage, { ...props })
is the way to go!
Hi @Huanzhang89 @njj I am stuck on the same issue. Can you please share a code snippet or example to fix it. Any help would be really appreciate! Thanks
@shoaibkhan94 here's the code example with their solution: https://codesandbox.io/s/62nk7x0p73
@longnt80 Thanks. It's working!
Sorry guys been away for a while, thanks @longnt80 for working it out and helping :)
Hey I saw this issue. Im having a somewhat similar problem. Im trying to access the values from a radiobutton and use that as an conditional rendering of a wizard.page
. For example Im wrapping pages like this:
<Wizard.Page validate={validators.validateFirstPage}>
{ props => (
<React.Fragment>
....more code here
And I have 3 radiobuttons on the first page and I would like to use the value of the radiobuttons to conditionally render pages like:
{ props => (
<React.Fragment>
{ props.values.radioGroup1 === 'daily' ? (
.... render content here
But it is not working. Im getting a TypeError: children is not a function
from my wizard class right here:
static Page = ({ children, parentState }) => {
return children(parentState); <---- from here.
};
I must be doing something wrong when trying to render base on the value of the radioGroup. Anyone tried anything like this?
@timrombergjakobsson
Hey Tim, can you log out what children is inside static Page
? Could be you cloned the activepage wrong and it Wizard.Page does not have access to this.props.children?
Can you also page your React.cloneElement
part of the form as that would shed some light on the issue!
@Huanzhang89 heres the React.cloneElement
part:
render() {
const { handleSubmit, children, prevButton, nextButton, classes } = this.props;
const { page, values } = this.state;
const activePage = React.Children.toArray(children)[page];
const isLastPage = page === React.Children.count(children) - 1;
const isFirstPage = page === 0;
const PrevButton = this.props.prevButton;
const NextButton = this.props.nextButton;
let currentStep = this.state.page;
console.log(values);
console.log(this.props.children, 'some children');
return (
<section className="site-main__content-wrapper">
<Header currentStep={currentStep}/>
<article className="site-main__content content-padding">
<div className="reminder__start">
<Formik
initialValues={this.state.values}
validate={this.validate}
enableReinitialize={false}
onSubmit={this.handleSubmit}
{ ...children }
render={props => (
<form className="reminder-form" onSubmit={props.handleSubmit}>
{React.cloneElement(activePage, { parentState: { ...props } })}
Okay so one thing I notice is that you are spreading children and assigning it to the props of Formik. You should remove that.
However the true culprit for your error is actually the fact that you spread props inside parentState. This means that your Page
no longer has access to this.props.children
.
{React.cloneElement(activePage, { props.children, parentState: { ...props } })}
would work or
{React.cloneElement(activePage, { parentState: props.parentState })}
assuming thats what you wanted to pass to parentState
@Huanzhang89 ah ok, so remove children from here: const { handleSubmit, children, prevButton, nextButton, classes } = this.props;
? What about this part { ...children }
? And is it not supposed to be {React.cloneElement(activePage, { props: props.children, parentState: { ...props } })}
and not {React.cloneElement(activePage, { props.children, parentState: { ...props } })}
?
Yes you should remove { ...children }
since that prop is not being used by formik.
For the second point I'll try to explain. The entire object of the second argument for React.cloneElement gets assigned to the props of the resulting React element. Remember the props of a React Element is simply an object, so in your case the props of your Element would be { props.children: Function, parentState: Object }
. And to access the children you would have to use this.props.props.children
which doesn't really make sense.
Did making that change fix your issue btw?
@Huanzhang89 I think Im with you, the only thing is that {React.cloneElement(activePage, { props.children, parentState: { ...props } })}
does not work, cos its syntax error. So I had to change to {React.cloneElement(activePage, { props: props.children, parentState: { ...props } })}
instead.
Ah sorry I made a mistake with that, it should be {React.cloneElement(activePage, { children: ...props.children, parentState: { ...props } })
But I would actually recommend not spreading ...props
into parentState
. Rather construct the parentState object before passing it to cloneElement here.
Ah alright, ok great thanks. Well yes I had to do a quite verbose and ugly solution to my problem:
<Wizard.Page validate={validators.validateSecondPage}>
{ props => (
<React.Fragment>
{ props.values.radioGroup1 === 'daily' ? ( <--- this is the condtional.
html here
Rather construct the parentState object before passing it to cloneElement
How do you mean?
You can check out this wizard component built for formik:
closing in favor of #1315
I wrote an example using Reactstrap (UI library) with Yup + Formik, demonstrating all the important features for a wizard:
https://github.com/vincentntang/multistep-wizard-formik-yup-reactstrap
I am using the multistep wizard from the examples section to implement a 2 page form but there is one small issue that I have and haven't been able to figure out.
The example showed how to easily hook up the input fields so that changes update automatically in the Wizard component's state.
For example I have a Field like the one below and I want the user's selection in this Field to affect the label of a sibling input. I am struggling to find a way to access the value of the Colour field.
I have successfully attached state of the Wizard component to each child component using the following code:
But what I am stuck on is how to access the props in Wizard.Page, since we are just calling the static method Page on the Wizard component and have no access to
this
.