expo-router 标题中的“保存”按钮

问题描述 投票:0回答:1

我是原生反应新手,正在尝试使用

expo-router
在模式标题中连接一个保存按钮。我可以让按钮在模式本身中执行我需要的操作,但我希望它位于从 expo-router 设置的标题中。将数据保存逻辑移至路由文件中似乎并不正确,即使我这样做,我也会收到这些领域上下文错误。所以我不知道如何继续。有没有办法通过世博会满足我的需要?在我看来,这个用例应该很常见,但我似乎在任何地方都找不到示例,包括他们的文档。

_layout.tsx

import React from 'react';
import { RealmProvider } from '@realm/react';
import { router, Stack } from 'expo-router';

import { HeaderButton } from '@components/common/fields/HeaderButton';
import { schemas } from '@models/index';

export default function Layout() {
  return (
    <RealmProvider schema={schemas} deleteRealmIfMigrationNeeded={true}>
      <Stack>
        <Stack.Screen
          name="index"
          options={{
            headerShown: false,
          }}
        />
        <Stack.Screen
          name="modal"
          options={{
            presentation: 'modal',
            headerTitle: () => (
              <HeaderButton displayText="<" handleClick={() => router.back()} />
            ),
            // headerRight: () => (
            //   <HeaderButton
            //     handleClick={}
            //     displayText="Save"
            //   />
            // ),
          }}
        />
      </Stack>
    </RealmProvider>
  );
}

modal.tsx

import React from 'react';
import { StatusBar } from 'expo-status-bar';
import { View } from 'react-native';

import { AddRecipeForm } from '@screens/recipe/AddRecipeForm';

export default function Modal() {
  return (
    <View>
      <StatusBar style="light" />
      <AddRecipeForm />
    </View>
  );
}

添加RecipeForm.tsx

import React, { useCallback } from 'react';
import { useRealm } from '@realm/react';
import { Controller, SubmitHandler, useForm } from 'react-hook-form';
import { StyleSheet, View } from 'react-native';

import { HeaderButton } from '@components/common/fields/HeaderButton';
import { TextField } from '@components/common/fields/TextField';
import { Recipe } from '@models/Recipe';
import colors from '@styles/colors';

interface AddRecipeFormProps {
  userId?: string;
}

interface FormData {
  userId?: string;
  description: string;
  name: string;
}

export const AddRecipeForm: React.FC<AddRecipeFormProps> = ({
  // toggleModal,
  userId,
}) => {
  const { control, handleSubmit } = useForm<FormData>();
  const realm = useRealm();

  const handleAddRecipe = useCallback(
    (name: string, description: string): void => {
      if (!description && !name) {
        return;
      }

      realm.write(() => {
        return realm.create(Recipe, {
          name,
          description,
          userId: userId ?? 'SYNC_DISABLED',
        });
      });
    },
    [realm, userId],
  );

  const onSubmit: SubmitHandler<FormData> = (data) => {
    handleAddRecipe(data.name, data.description);
  };

  return (
    <>
      <View style={styles.container}>
        <View style={styles.buttonContainer}>
          <HeaderButton
            displayText="Save"
            handleClick={handleSubmit(onSubmit)}
          />
          {/* <CustomButton displayText="Cancel" handleClick={toggleModal} /> */}
        </View>
        <Controller
          control={control}
          // rules={{ required: true }}
          render={({ field: { onChange, value } }) => (
            <TextField
              value={value}
              onChangeText={onChange}
              placeholder="name"
            />
          )}
          name="name"
        />
        <Controller
          control={control}
          // rules={{ required: true }}
          render={({ field: { onChange, value } }) => (
            <TextField
              value={value}
              onChangeText={onChange}
              placeholder="description"
            />
          )}
          name="description"
        />
      </View>
    </>
  );
};

tldr;更具体地说,我想移动

HeaderButton
及其所有功能,以在
_layout.tsx
堆栈中定义的模式标头中使用。将所有逻辑从组件移到布局中似乎并不合适(我也无法让它工作)。有办法实现吗?

typescript react-native expo react-hook-form expo-router
1个回答
0
投票

我终于明白了。我实际上是通过阅读

expo-router
文档来计算出
react-navigation
的。 https://reactnavigation.org/docs/header-buttons/#header-interaction-with-its-screen-component

本质上,您在

_layout.tsx
中声明它,并使用
navigation.setOptions
在实际组件中“覆盖”它。请参阅下面两个文件中的
headerRight
道具。

_layout.tsx

export default function Layout() {
  const router = useRouter();
  return (
    <RealmProvider schema={schemas} deleteRealmIfMigrationNeeded={true}>
      <Stack>
        <Stack.Screen
          name="index"
          options={{
            headerShown: false,
          }}
        />
        <Stack.Screen
          name="modal"
          options={{
            presentation: 'modal',
            headerTitle: 'Add Recipe',
            headerLeft: () => (
              <HeaderButton handleClick={() => router.back()} displayText="<" />
            ),
            headerRight: () => <HeaderButton displayText="Save" />,
          }}
        />
      </Stack>
    </RealmProvider>
  );
}

添加RecipeForm.tsx

export const AddRecipeForm: React.FC<AddRecipeFormProps> = ({
  // toggleModal,
  userId,
}) => {
  const { control, handleSubmit } = useForm<FormData>();
  const realm = useRealm();
  const navigation = useNavigation();
  const router = useRouter();

  const handleAddRecipe = useCallback(
    (name: string, description: string): void => {
      if (!description && !name) {
        return;
      }

      realm.write(() => {
        return realm.create(Recipe, {
          name,
          description,
          userId: userId ?? 'SYNC_DISABLED',
        });
      });
    },
    [realm, userId],
  );

  useEffect(() => {
    navigation.setOptions({
      headerRight: () => (
        <HeaderButton displayText="Save" handleClick={handleSubmit(onSubmit)} />
      ),
    });
  }, [navigation]);

  const onSubmit: SubmitHandler<FormData> = (data) => {
    handleAddRecipe(data.name, data.description);
    router.back();
  };

  return (
    <View style={styles.container}>
      <Controller
        control={control}
        // rules={{ required: true }}
        render={({ field: { onChange, value } }) => (
          <TextField value={value} onChangeText={onChange} placeholder="name" />
        )}
        name="name"
      />
      <Controller
        control={control}
        // rules={{ required: true }}
        render={({ field: { onChange, value } }) => (
          <TextField
            value={value}
            onChangeText={onChange}
            placeholder="description"
          />
        )}
        name="description"
      />
    </View>
  );
};

希望这对某人有帮助,尤其是在

expo
文档如此稀疏的情况下。

© www.soinside.com 2019 - 2024. All rights reserved.