Implement a flow custom navigator #11839
-
I'm working on a custom navigator to handle flows, and I encounter a bug that I'm not sure how to solve. I've thought of 2 solutions and I'd like to hear your opinion A flow navigatorA Flow Navigator would simplify navigation flow management by providing an API that abstracts the complexity of managing navigation flows. It allows individual screens to navigate through the flow using simple methods like This way, we could get a comprehensive overview of a flow in a single location: in the screen stack declaration. This would allows new developers to quickly understand the entire flow without the need to examine each page individually. At BAM, we've identified a recurring need for a flow navigator. Recently, I implemented a state machine with xState to manage a flow. However, the flow's state (which page is currently focused) was duplicated between xState and the navigation state. A simplified implementation can be viewed in this repository: https://github.com/charlotteisambert/yokoten-xstate-navigation A custom navigator could eliminate state duplication by using a state machine-like API. Use cases include:
Ideal APIBasic usageconst FlowNavigator = createFlowNavigator();
export const FlowNavigatorExample = () => {
// Define your screens and their order in the flow
return (
<FlowNavigator.Navigator screenOptions={{ headerShown: false }}>
<FlowNavigator.Screen name="Step1" component={Step1Page} />
<FlowNavigator.Screen name="Step2" component={Step2Page} />
<FlowNavigator.Screen name="Step3" component={Step2Page} />
</FlowNavigator.Navigator>
);
}; In each screen component, you can navigate through the flow using: const Step1Page = () => {
const { goToNextStep, goToPreviousStep } = useNavigation();
const { currentStep } = useFlowStatus();
return (
<Button title="Go to next page" onPress={() => goToNextStep()} />
)
}; Define conditional stepsIn certain scenarios, a flow may include steps that are conditional. These steps might be dependent on user-specific conditions or based on whether certain actions have already been completed. You can manage such conditional steps declaratively in your navigation flow. Here's an example where "Step 2" is conditionally displayed based on the flag variable. This variable could be a piece of data fetched from the backend or a state within your application. import {atom, useAtom} from 'jotai';
const FlowNavigator = createFlowNavigator();
export const flag = atom(/* your condition here */); // you can use other state libraries
export const FlowNavigatorExample = () => {
const [flag, _] = useAtom();
return (
<FlowNavigator.Navigator screenOptions={{ headerShown: false }}>
<FlowNavigator.Screen name="Step1" component={Step1Page} />
{flag && <FlowNavigator.Screen name="Step2" component={Step2Page} />}
<FlowNavigator.Screen name="Step3" component={Step3Page} />
</FlowNavigator.Navigator>
);
}; In this example, the Step2 screen is only included in the flow if flag evaluates to true. Where we’re atWe’ve implemented a first version of this ideal API, available [here](https://github.com/bamlab/flow-navigator/tree/ideal-api-dumb-implementation) But we’ve encountered a bug with this implementation that we don’t know how to solve that easily. I’ve described the problem and the 2 alternative solutions we’ve thought about in here: (thought it was easier to explain with schemas) https://app.eraser.io/workspace/3tmp5gmEQpbBwVi4Cuzh?origin=share We’d love to hear your feedback:
Thanks a lot! |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 2 replies
-
In your example, you have this code: setFlag(true)
goToNextStep() When you cal So you'd somehow need to wait for the re-render to complete (e.g. some event in setFlag(true);
waitForFlag(goToNextStep); |
Beta Was this translation helpful? Give feedback.
-
@charlotteisambert How does your solution actually work? Im trying to solve a similar problem, but using setTimeout(.., 0) doesn't change anything. Im not using a react-navigation to do that tho. |
Beta Was this translation helpful? Give feedback.
In your example, you have this code:
When you cal
setFlag(true)
, it will update state and trigger a re-render asynchronously since state updates are async in React. But here you're callinggoToNextStep()
synchronously, right aftersetFlag(true)
. By this time, the flag hasn't actually updated and the re-render hasn't happened. This would explain why you're going to step 3 instead of step 2.So you'd somehow need to wait for the re-render to complete (e.g. some event in
useEffect
) before you can callgoToNextStep()
, e.g.