newbreedofgeek / react-stepzilla

A React multi-step/wizard component for sequential data collection
https://newbreedofgeek.github.io/react-stepzilla/
ISC License
618 stars 176 forks source link

How to persist Step State on "Previous/Back" button click #63

Closed gate3 closed 7 years ago

gate3 commented 7 years ago

Hi, great library. This is my first time using this, i've tried to go through other issues but can't find what i need. The issue is that the data filled in, into a previous step goes missing as soon as i go to another step, i don't know if this is by design or an error, because wizard steps should normally retain state so users can go back. If this is by design, then i'd be really happy if i can get any suggestions on how to overcome this issue. Thanks.

newbreedofgeek commented 7 years ago

@gate3 this is by design. You make a valid point that a wizard should retain data going back too, but currently how it works is that clicking "next" calls a injected "isValidated()" method in that step and it's up to you on validating data and saving to state. The decision was made to keep it simple, as validation is involved and you need to prompt the user to fix data before saving to state, I only call isValidated() on "Next" where the user is committed to fixing issues and moving forward. Promoting users to fix issues on "back" is a broken UX - but I do see a use case for this.

At the moment clicking back does not trigger any local method so you can't save it to state unfortunately. You are welcome to make this an "option" and if your up for it you can raise a PR.

Here is the recommended way to make this an option: 1) Create a new StepZilla option called "callValidatedOnBack", default this to false 2) in StepZilla source, check where isValidated is called - I will have a check to only call this on "next" - if the user has set callValidatedOnBack to true then call it on back as well. 3) if it's called on back, then pass in a prop to isValidated that indicates to the Step component that it was triggered by back and then you can decide on if you should trigger validation in your step or just save to state (if it's a back)

Hope that helps

gate3 commented 7 years ago

OK, seems like a good option. But I am thinking of saving to state using

componentWillUnmount

and then recovering from state in

componentWillMount

But for now, i want to attempt keeping state data in parent.

I'd like to know your thoughts about this please.

gate3 commented 7 years ago

Storing in parent state seems to work like a charm. What i'm doing is saving to parent state on using

componentWillUnmount

and then i passed in the particular state as props to each step. Then I'm setting the input text value as an uncontrolled input using

defaultValue

peek 2017-08-09 01-04

newbreedofgeek commented 7 years ago

@gate3 brilliant, of course. componentWillUnmount is the best way to do this as it puts the Step in full control and we dont need any core code changes to support this. Thanks for doing this. I will close this now.

gate3 commented 7 years ago

Oh no thank you for a wonderful library.. Saved me a lot of time. Thanks On 9 Aug 2017 7:09 am, "Mark Paul" notifications@github.com wrote:

@gate3 https://github.com/gate3 brilliant, of course. componentWillUnmount is the best way to do this as it puts the Step in full control and we dont need any core code changes to support this. Thanks for doing this. I will close this now.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/newbreedofgeek/react-stepzilla/issues/63#issuecomment-321162119, or mute the thread https://github.com/notifications/unsubscribe-auth/ADbkfQbHJF9OTcZCzxsnRUoSIOz843Whks5sWU0EgaJpZM4OwQsd .

gate3 commented 7 years ago

I'd like to add an example to of how I solved this. Should I make a PR. On 9 Aug 2017 7:32 am, "Tayo olarewaju" youngenterpreneur11@gmail.com wrote:

Oh no thank you for a wonderful library.. Saved me a lot of time. Thanks On 9 Aug 2017 7:09 am, "Mark Paul" notifications@github.com wrote:

@gate3 https://github.com/gate3 brilliant, of course. componentWillUnmount is the best way to do this as it puts the Step in full control and we dont need any core code changes to support this. Thanks for doing this. I will close this now.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/newbreedofgeek/react-stepzilla/issues/63#issuecomment-321162119, or mute the thread https://github.com/notifications/unsubscribe-auth/ADbkfQbHJF9OTcZCzxsnRUoSIOz843Whks5sWU0EgaJpZM4OwQsd .

newbreedofgeek commented 7 years ago

Good idea, but a PR is not needed. I will tag this Ticket "Useful Tips" and link it off the README/WIKI for others to see.

So please add your example to this ticket.

gate3 commented 7 years ago

OK will do thanks On 10 Aug 2017 6:36 am, "Mark Paul" notifications@github.com wrote:

Good idea, but a PR is not needed. I will tag this Ticket "Useful Tips" and link it off the README/WIKI for others to see.

So please add your example to this ticket.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/newbreedofgeek/react-stepzilla/issues/63#issuecomment-321455552, or mute the thread https://github.com/notifications/unsubscribe-auth/ADbkfQx_2p9nisbNQTedSlHyIPCLiSKXks5sWpbvgaJpZM4OwQsd .

newbreedofgeek commented 7 years ago

@gate3 WIKI pages added - https://github.com/newbreedofgeek/react-stepzilla/wiki/Useful-Dev-Tips

gate3 commented 7 years ago

Say we have a parent component that uses stepzilla like this

const steps = [ {name:'Step1',component:<Step1 />} {...} ]

Create a state for the step in parent component

this.state = { step1:{someData:'',someData2:''} }

Create a function to collect data from the child component during 'componentWillUnmount'. Assign an id for each child so you can use a switch statement to know which child is passing the data, then overwrite the state in the parent

saveChildData (childId, dataObject){ switch (id){ case 1: this.setState({ step1:dataObject }) break; ... } }

Ensure you remember to break when using switch, i forgot to at first and it cost me some time before i could realize what was wrong.

Then modify the call to stepzilla component, passing the data from parent, id and the saveChildData function.

{name:'Step1',component:<Step1 id={1} storeFunc={this.saveChildData.bind(this) {...this.state.step1} />}

Then in the child component simply create a component to pass the data back to parent on componentWillUnload and also pass the data to your form components. You can't use a controlled input element bcos assigning props to it will mean you can't edit, so use an uncontrolled one.

<input defaultValue={this.props.someData} ref={(input) => this.someVal = input} />

Then the function to save the data goes like this

componentWillUnmount () { this.props.storeFunc({someData:this.someVal.value,... }

Thats it.

Note when trying to store data for an array of components, use the child state and restore the components using 'componentDidMount'

newbreedofgeek commented 7 years ago

@gate3 your contribution is very much appreciated!

moisesnandres commented 6 years ago

Hi @gate3 can you do an example on a gist? I tried to follow your steps but didn't work

rickerd commented 6 years ago

@gate3 is there an example somewhere? I can't get it to work and where to place the functions.

vishagar81 commented 6 years ago

@newbreedofgeek I am just unable to get this working. Sadly, I do not get any errors too. I have followed your description but not sure whether I am missing something.

sfblaauw commented 5 years ago

Hi! I'm new using Reactjs but here is my gist for persisting data. Please @newbreedofgeek and @gate3 let me know if I'm in the correct path. Thanks for the module, it is awesome!

mrkchang commented 4 years ago

I don't think this works for going backwards

packaging03 commented 2 years ago

This is excatly what I did that make it workfor normal text input field. However, it does not work on