In Jupyter I approach this by structuring my exploratory analysis in sections, with the minimum of variables reused between sections.
Typically the time-intensive data prep stage is section 1.
The remaining sections are designed essentially like function blocks: data inputs listed in the first cell and data outputs/visualizations towards the end.
Once I decide the exploratory analysis in a section is more-or-less right, I bundle up the code cells into a standalone function, ready for reuse later in my analysis.
Jupyter notebooks can easily get disorganised with out-of-order state. However that is their strength too: exploratory analysis and trying different code approaches is inherently a creative rather than a linear activity.