NativeScript React集成 #
React 集成概述 #
NativeScript 与 React 结合,让你可以使用 React 的组件化思想开发原生应用。
text
┌─────────────────────────────────────────────────────────────┐
│ React NativeScript │
├─────────────────────────────────────────────────────────────┤
│ │
│ 核心特性 │
│ ├── React 18 支持 │
│ ├── Hooks API │
│ ├── JSX 语法 │
│ ├── 函数式组件 │
│ └── Redux 状态管理 │
│ │
│ NativeScript 扩展 │
│ ├── 原生 UI 组件 │
│ ├── 原生路由 │
│ └── 原生模块 │
│ │
└─────────────────────────────────────────────────────────────┘
项目创建 #
创建 React 项目 #
bash
ns create my-app --react
项目结构 #
text
my-app/
├── app/
│ ├── App.tsx
│ ├── components/
│ │ ├── Home.tsx
│ │ └── Detail.tsx
│ ├── hooks/
│ │ └── useUser.ts
│ └── store/
│ └── index.ts
├── App_Resources/
├── package.json
└── tsconfig.json
应用入口 #
App.tsx #
tsx
// App.tsx
import * as React from 'react';
import { Frame } from '@nativescript/core';
import { Home } from './components/Home';
export function App() {
return (
<Frame>
<Home />
</Frame>
);
}
注册应用 #
typescript
// main.ts
import { Application } from '@nativescript/core';
import { App } from './App';
Application.run({
create: () => {
return App();
}
});
组件开发 #
函数式组件 #
tsx
// components/Home.tsx
import * as React from 'react';
import { Page, ActionBar, ActionItem, ListView, Label, Button, GridLayout, StackLayout } from '@nativescript/core';
interface Item {
id: number;
title: string;
price: string;
}
export function Home() {
const [items, setItems] = React.useState<Item[]>([
{ id: 1, title: 'Item 1', price: '$10' },
{ id: 2, title: 'Item 2', price: '$20' },
{ id: 3, title: 'Item 3', price: '$30' }
]);
const addItem = () => {
setItems([
...items,
{
id: items.length + 1,
title: `Item ${items.length + 1}`,
price: '$0'
}
]);
};
const onItemTap = (item: Item) => {
console.log('Tapped:', item);
};
const renderListItem = (item: Item) => (
<GridLayout columns="*, auto" onTap={() => onItemTap(item)} key={item.id}>
<Label text={item.title} col={0} />
<Label text={item.price} col={1} />
</GridLayout>
);
return (
<Page>
<ActionBar title="Home">
<ActionItem text="Add" onTap={addItem} />
</ActionBar>
<GridLayout>
<ListView items={items} cellFactory={renderListItem} />
</GridLayout>
</Page>
);
}
使用 Hooks #
tsx
// components/UserProfile.tsx
import * as React from 'react';
import { Page, ActionBar, GridLayout, StackLayout, Image, Label, Button, TextField } from '@nativescript/core';
interface User {
name: string;
email: string;
avatar: string;
}
export function UserProfile({ userId }: { userId: number }) {
const [user, setUser] = React.useState<User>({
name: 'John Doe',
email: 'john@example.com',
avatar: '~/assets/avatar.png'
});
const [editMode, setEditMode] = React.useState(false);
const [editName, setEditName] = React.useState(user.name);
const [editEmail, setEditEmail] = React.useState(user.email);
const saveProfile = () => {
setUser({
...user,
name: editName,
email: editEmail
});
setEditMode(false);
};
return (
<Page>
<ActionBar title="Profile" />
<GridLayout>
<StackLayout>
<Image src={user.avatar} width={100} height={100} borderRadius={50} />
<Label text={user.name} className="h2" />
<Label text={user.email} className="text-muted" />
{!editMode ? (
<Button text="Edit" onTap={() => setEditMode(true)} />
) : (
<StackLayout>
<TextField text={editName} onTextChange={(e) => setEditName(e.value)} hint="Name" />
<TextField text={editEmail} onTextChange={(e) => setEditEmail(e.value)} hint="Email" keyboardType="email" />
<Button text="Save" onTap={saveProfile} />
<Button text="Cancel" onTap={() => setEditMode(false)} />
</StackLayout>
)}
</StackLayout>
</GridLayout>
</Page>
);
}
自定义 Hooks #
tsx
// hooks/useUser.ts
import * as React from 'react';
import { UserService } from '../services/user.service';
interface User {
id: number;
name: string;
email: string;
}
interface UseUserResult {
user: User | null;
loading: boolean;
error: Error | null;
refetch: () => Promise<void>;
}
export function useUser(userId: number): UseUserResult {
const [user, setUser] = React.useState<User | null>(null);
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState<Error | null>(null);
const userService = React.useMemo(() => new UserService(), []);
const fetchUser = React.useCallback(async () => {
setLoading(true);
setError(null);
try {
const data = await userService.getUser(userId);
setUser(data);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
}, [userId, userService]);
React.useEffect(() => {
fetchUser();
}, [fetchUser]);
return {
user,
loading,
error,
refetch: fetchUser
};
}
tsx
// 使用自定义 Hook
import * as React from 'react';
import { useUser } from '../hooks/useUser';
export function UserDetail({ userId }: { userId: number }) {
const { user, loading, error, refetch } = useUser(userId);
if (loading) {
return <ActivityIndicator busy={true} />;
}
if (error) {
return (
<StackLayout>
<Label text={`Error: ${error.message}`} />
<Button text="Retry" onTap={refetch} />
</StackLayout>
);
}
return (
<StackLayout>
<Label text={user?.name} />
<Label text={user?.email} />
</StackLayout>
);
}
导航 #
页面导航 #
tsx
// components/Home.tsx
import * as React from 'react';
import { Frame, Page, ActionBar, Button, GridLayout } from '@nativescript/core';
import { Detail } from './Detail';
export function Home() {
const goToDetail = (id: number) => {
Frame.topmost().navigate({
create: () => <Detail id={id} />,
transition: { name: 'slide' }
});
};
return (
<Page>
<ActionBar title="Home" />
<GridLayout>
<Button text="Go to Detail" onTap={() => goToDetail(1)} />
</GridLayout>
</Page>
);
}
// components/Detail.tsx
import * as React from 'react';
import { Frame, Page, ActionBar, NavigationButton, GridLayout, Label } from '@nativescript/core';
interface DetailProps {
id: number;
}
export function Detail({ id }: DetailProps) {
const goBack = () => {
Frame.topmost().goBack();
};
return (
<Page>
<ActionBar title="Detail">
<NavigationButton text="Back" onTap={goBack} />
</ActionBar>
<GridLayout>
<Label text={`Item ID: ${id}`} />
</GridLayout>
</Page>
);
}
状态管理 #
使用 Context #
tsx
// context/AppContext.tsx
import * as React from 'react';
interface User {
id: number;
name: string;
email: string;
}
interface AppState {
user: User | null;
theme: 'light' | 'dark';
}
interface AppContextType extends AppState {
setUser: (user: User | null) => void;
setTheme: (theme: 'light' | 'dark') => void;
}
const AppContext = React.createContext<AppContextType | undefined>(undefined);
export function AppProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = React.useState<User | null>(null);
const [theme, setTheme] = React.useState<'light' | 'dark'>('light');
const value = {
user,
theme,
setUser,
setTheme
};
return (
<AppContext.Provider value={value}>
{children}
</AppContext.Provider>
);
}
export function useAppContext() {
const context = React.useContext(AppContext);
if (!context) {
throw new Error('useAppContext must be used within AppProvider');
}
return context;
}
tsx
// 使用 Context
import * as React from 'react';
import { useAppContext } from '../context/AppContext';
export function UserProfile() {
const { user, setUser } = useAppContext();
return (
<StackLayout>
{user ? (
<>
<Label text={user.name} />
<Label text={user.email} />
<Button text="Logout" onTap={() => setUser(null)} />
</>
) : (
<Button text="Login" onTap={() => {/* login logic */}} />
)}
</StackLayout>
);
}
使用 Redux #
tsx
// store/index.ts
import { createStore } from 'redux';
interface User {
id: number;
name: string;
email: string;
}
interface AppState {
user: User | null;
items: any[];
isLoading: boolean;
}
const initialState: AppState = {
user: null,
items: [],
isLoading: false
};
function reducer(state = initialState, action: any) {
switch (action.type) {
case 'SET_USER':
return { ...state, user: action.payload };
case 'SET_ITEMS':
return { ...state, items: action.payload };
case 'SET_LOADING':
return { ...state, isLoading: action.payload };
default:
return state;
}
}
export const store = createStore(reducer);
// Hooks
import { useSelector, useDispatch } from 'react-redux';
export function useUser() {
return useSelector((state: AppState) => state.user);
}
export function useItems() {
return useSelector((state: AppState) => state.items);
}
export function useActions() {
const dispatch = useDispatch();
return {
setUser: (user: User | null) => dispatch({ type: 'SET_USER', payload: user }),
setItems: (items: any[]) => dispatch({ type: 'SET_ITEMS', payload: items }),
setLoading: (isLoading: boolean) => dispatch({ type: 'SET_LOADING', payload: isLoading })
};
}
组件通信 #
Props 传递 #
tsx
// 父组件
export function Parent() {
const [data, setData] = React.useState('Hello');
const handleUpdate = (newValue: string) => {
setData(newValue);
};
return (
<GridLayout>
<Child data={data} onUpdate={handleUpdate} />
</GridLayout>
);
}
// 子组件
interface ChildProps {
data: string;
onUpdate: (value: string) => void;
}
export function Child({ data, onUpdate }: ChildProps) {
return (
<Label text={data} onTap={() => onUpdate('New Value')} />
);
}
最佳实践 #
组件设计原则 #
text
┌─────────────────────────────────────────────────────────────┐
│ 组件设计原则 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 函数式组件 │
│ 使用函数式组件和 Hooks │
│ │
│ 2. 单一职责 │
│ 每个组件只做一件事 │
│ │
│ 3. Props 类型定义 │
│ 使用 TypeScript 定义 Props 类型 │
│ │
│ 4. 合理拆分 │
│ 大组件拆分为小组件 │
│ │
│ 5. 性能优化 │
│ 使用 useMemo 和 useCallback │
│ │
└─────────────────────────────────────────────────────────────┘
下一步 #
现在你已经掌握了 React 集成,接下来学习 部署发布,了解如何发布你的应用!
最后更新:2026-03-29