RN پکیج های

React Native packages#

Navigation#

می توان توسط این پکیج routing را در react native پیاده سازی کرد ک حتما داکیومنت اصلی آن خوانده شود:

Navigation

yarn add @react-navigation/native

حال اگر در expo کار می کنیم باید موارد زیر را نیز نصب کنیم:

expo install react-native-screens react-native-safe-area-context

اما اگر از خود react native cli استفاده می کنیم باید بجای دستور بالا از دستور زیر استفاده کنیم:

yarn add react-native-screens react-native-safe-area-context

حال می توان در پروژه خود یک فولدر ب نام screens (دلخواه) بسازیم و صفحاتی ک می خواهیم را در آن بسازیم و از این پکیج برای جابه جا شدن در آن ها استفاده کنیم.

🎉 در react native به 2 صورت می توان از navigation استفاده کرد ک 1. Drawer و 2. stack می باشد ک اولی به صورت منوی کشویی می باشد و دومی هم می توان ب صورت لینک در جاهای مختلف برنامه استفاده کرد.

Stack#

برای استفاده از Stack باید آن را نصب کنیم:

yarn add @react-navigation/stack

🎉 حتما بعد از نصب این پکیج باید پکیج زیر را نیز توسط خود expo نصب کنیم تا بر اساس آخرین ورژن مورد نیاز برای expo sdk آن را نصب کند:

expo install react-native-gesture-handler

حال باید ب صورت زیر عمل کرد:

  1. ابتدا باید کل برنامه را داخل NavigationContainer پیچید (مانند BrowserRouter در ری اکت)
import {NavigationContainer} from "@react-navigation/native";
export default function App() {
return (
<NavigationContainer>
{/* routes */}
</NavigationContainer>
);
}
  1. حال باید از پکیج stack ک نصب کردیم استفاده کنیم و route هایمان را بسازیم ک ب صورت پیش فرض برای ما یک header می سازد:
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import Home from "./screens/Home";
import About from "./screens/About";
const Stack = createStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="About" component={About} />
</Stack.Navigator>
</NavigationContainer>
);
}

ک در اینجا name اجباری می باشد و در هر صفحه و کامپوننتی باید از نام آن ها برای جابه جا شدن کاربر استفاده کنیم.

🎉 همچنین یک پکیج دیگر ب نام react-navigation/native-stack در داکیومنت های خود navigation وجود دارد ک می توان بجای این پکیج استفاده کرد ک سرعت بالا تری دارد اما زیاد نمی توان آن را شخصی سازی کرد.

  1. حال در هر کدام از صفحات می توان از props هایی ک خود NavigationContainer می فرستد استفاده کرد و دکمه برای جابه جایی بین صفحات قرار داد:
export default function Home(props) {
const {navigation} = props;
return (
<View style={styles.container}>
<Text>Home</Text>
<Button title="Move to About" onPress={() => navigation.navigate("About")}/>
</View>
);
}

🎉 مواردی ک ارسال می شود توسط NavigationContainer شامل موارد بسیاری می باشد ک اگر لاگ بگیریم، موارد زیادی دارد ک همه تابع هستند:

canGoBack: برای چک کردن زمانی ک آیا می توان ب صفحه قبل بازگشت یا خیر

goBack: زمانی ک بخواهیم ب یک صفحه قبل بگردیم

isFocused: زمانی ک بخواهیم چک کنیم ک آیا کاربر در صفحه مورد نظر ما است

navigate: برای جابه جا شدن ب صفحه ای ک ما مشخص می کنیم

pop: برای حذف یک صفحه ک به stack پوش شده است (در واقع stack یک استوانه است ک صفحات ب آن push می شوند و صفحات جدید روی صفحات قبلی قرار می گیرند یعنی از ته پر می شود. )

popToTop: برای زمانی ک بخواهیم کل موارد داخل stack را حذف کنیم و مستقیما ب آخرین صفحه برویم ک همان صفحه اول می باشد(یعنی اولین push).

🎉 اگر از push استفاده کنیم، در واقع ب stack یک صفحه ای را اضافه کرده ایم و فرق این با navigate در این است ک push می تواند صفحه ای ک در آن هستیم را دوباره ب stack ب تعداد دلخواه اضافه کند و صفحه را reload می کند ک در این حالت می توان توسط goBack در صفحات اضافه شده در stack جابه شویم و همان صفحه را دوباره ببینیم فقط با این فرق ک انگار صفحه reload می شود.

replace: زمانی استفاده می شود ک بخواهیم مثلا در صفحه لاگین، بعد از ورود کاربر، دیگر دکمه بازگشتی نمایش داده نشود و صفحه مورد نظر ما بشود اولین صفحه stack مان

send prop to another page#

ما می توانیم به صورت بین صفحات، داده ارسال کنیم:

// Home.jsx
<Button title="Move to Details" onPress={() => navigation.navigate("Details", {id: 1, text: "Hello"})}/>

و ب این شکل داده را در صفحه مورد نظر توسط route آن را دریافت کنیم:

export default function Details({route}) {
const {id, text} = route.params;
return (
<View style={styles.container}>
<View style={styles.separator}>
<Text>{id}</Text>
<Text>{text}</Text>
</View>
</View>
);
}

🎉 همچنین می توان در app.jsx نیز هنگام تعریف stack مورد نظر، برای آن یک initialParams نیز تعریف کنیم ک اگر داده ما وجود نداشت، داده ثابت ما نمایش داده شود:

// app.jsx
<Stack.Screen name="Details" component={Details} initialParams={{id: 3, text: "Bye Bye"}} />

header style#

ما می توانیم به صورت زیر ب header صفحاتمان استایل بدهیم و این استایل را می توان صفحه ب صفحه بدهیم و یا ب صورت کلی یک استایل را برای همه اعمال کنیم:

<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerTitleAlign: "center",
headerTintColor: "#fff",
headerStyle: {
backgroundColor: "#f4511e",
},
headerTitleStyle: {
fontWeight: "bold",
},
}}>
<Stack.Screen name="Home" component={Home} options={{
title: "خانه",
headerStyle: {
backgroundColor: "red"
}
}} />
<Stack.Screen name="About" component={About} />
<Stack.Screen name="Details" component={Details} initialParams={{ id: 3, text: "Bye Bye" }} options={{
title: "جزئیات",
}}
/>
</Stack.Navigator>
</NavigationContainer>

🎉 می توان استایل های screenOptions را ک ب صورت عمومی ب همه ی صفحات اعمال می شود را برداریم و برای هر صفحه در قسمت options آن استایل ها را بدهیم و هر کدام را متفاوت قرار دهیم.

همچنین موارد بیشتری برای تغییر دادن وجود دارد ک می توانیم خودمان تست کنیم و یا از مستندات استفاده کنیم.

🎉 همچنین استایل هایی ک برای هر route قرار می دهیم، بر استایل هایی ک در screenOptions قرار می دهیم ارجعیت دارد.

🎉 همچنین می توان از مواردی ک props.navigation در صفحات ب ما می دهد نیز برای تغییر header هر صفحه استفاده کنیم، مثلا با کلیک روی یک دکمه، رنگ و یا نوشته هدر ما تغییر کند:

// Details.jsx
<Button title='بروز رسانی هدر' onPress={() => navigation.setOptions({
title: "ممد"
})}/>

🎉 همچنین می توان در سمت راست یا چپ header نیز از یک کامپوننت استفاده کرد یا یک jsx خاص قرار داد تا نمایش دهد و حتی یک کار خاصی انجام دهد:

<Stack.Screen name="About" component={About}
options={{
headerRight: () => (
<Button title="Alert" color="orange" onPress={() => alert("سلام")}/>
),
}} />

🎉 همچنین ما می توانیم یک کامپوننت را بجای headerTitle و جاهای دیگر نیز قرار دهیم و تمامی props هایی ک آن قسمت دارد را نیز برای آن کامپوننت بفرستیم تا در آنجا از آن ها استفاده کنیم:

const LogoTitle = () => {
return (
<Image style={{width: 50, height: 50}} source={require("./assets/icon.png")}/>
)
}
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={Home}
options={{
title: "خانه",
headerStyle: {
backgroundColor: "red",
},
headerTitle: (props) => <LogoTitle {...props}/>
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
}

Drawer#

برای استفاده از drawer باید مانند stack ابتدا پکیج مربوط ب آن را از navigation نصب کنیم:

yarn add @react-navigation/drawer

🎉 حتما بعد از نصب این پکیج باید پکیج های زیر را نیز توسط خود expo نصب کنیم تا بر اساس آخرین ورژن مورد نیاز برای expo sdk آن ها را نصب کند:

expo install react-native-gesture-handler react-native-reanimated

سپس بعد از نصب این 2 پکیج باید babel.config.json را نیز تغییر دهیم:

module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: ["react-native-reanimated/plugin",],
};
};

🎉 اگر با ارور زیر مواجه شدیم، باید دستور "مرحله آخر" را بزنیم:

Reanimated 2 failed to create a worklet, maybe you forgot to add Reanimated's babel plugin

و در مرحله آخر نیز باید یک بار سرور metro را نیز ریست کنیم:

npx react-native start --reset-cache

حال می توان از این پکیج استفاده کرد:

const Drawer = createDrawerNavigator();
export default function App() {
return (
<NavigationContainer>
<Drawer.Navigator
initialRouteName="Home"
screenOptions={{
drawerPosition: "right", // برای باز شدن منو از سمت راست
drawerStyle: { backgroundColor: "orange", }, // استایل دادن ب خود منو
drawerActiveTintColor: "white", // رنگ نوشته منوی اکتیو
drawerInactiveTintColor: "gray", // رنگ نوشته منوی غیر اکتیو
headerShown: true, // نمایش یا عدم نمایش منوی هدر
headerTitleAlign: "center",
drawerLabelStyle: {textAlign: "center"}, // برای استایل دادن ب تک تک آیتم های داخل منو
headerRight: (props) => { // اضافه کردن هر چیزی ب سمت راست هدر
return (
<View style={{marginRight: 10}}>
<Ionicons onPress={() => alert("mamad")} name="menu" size={32} color="black" />
</View>
)
},
headerLeft: () => {} }} // اضافه کردن هرچیزی ب سمت چپ منو
>
<Drawer.Screen name="Home" component={Home} />
<Drawer.Screen name="Information" component={Information} options={{ title: "اطلاعات", headerTitle: "اطلاعات", }} />
</Drawer.Navigator>
</NavigationContainer>
);
}

🎉 مابقی موارد دقیقا مانند stack می باشد.

Bottom tab navigation#

ابتدا باید پکیج مربوط ب آن را نصب کنیم:

yarn add @react-navigation/bottom-tabs

🎉 فرم کلی استفاده از این 3 حالت برای navigate کردن شبیه ب هم هستند.

حال می توان مانند بقیه حالت ها موارد لازم را استفاده کنیم:

const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={Home}/>
<Tab.Screen name="Information" component={Information}/>
</Tab.Navigator>
</NavigationContainer>
);
}

که در قسمت پایین صفحه برای ما یک قسمت برای navigate می آورد ک آیکون هایش باید عوض شود و می توان آن را کامل شخصی سازی کرد:

<NavigationContainer>
<Tab.Navigator screenOptions={({route}) => ({ // توسط اینکار ما می توانیم ب روت ها دسترسی داشته باشیم
tabBarIcon: ({focused, color, size}) => {
let iconName; // توسط شرط های زیر و شرایط مختلف می توان مقدار خاصی را برای آیکون ها قرار داد
if(route.name === "Home") {
iconName = focused ? "ios-information-circle" : "ios-information-circle-outline"
} else if (route.name === "Information") {
iconName = focused ? "ios-list" : "ios-list-outline"
}
return (
<Ionicons name={iconName} size={size} color={color}/>
)
},
tabBarActiveTintColor: "tomato", // رنگ حالت اکتیو
tabBarInactiveTintColor: "gray", // رنگ حالت دی اکتیو
})}>
<Tab.Screen name="Home" component={Home}/>
<Tab.Screen name="Information" component={Information} options={{
title: "اطلاعات",
tabBarShowLabel: true, // اگر فالس باشد، هنگام فوکوش شدن دیگر لیبل آن نمایش داده نمی شود
tabBarHideOnKeyboard: true,
tabBarStyle: {
display: "none" // اگر وارد آن روت شویم، کل منوی پایین محو می شود
},
}}/>
</Tab.Navigator>
</NavigationContainer>

🎉 همچنین میتوان برای تغییر آیکون ها از options تک تک route ها نیز استفاده کرد.

nested navigation#

می توان برای اینکه از چند مدل navigation استفاده کنیم ب صورت زیر عمل کنیم:

// App.js
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} options={{ headerShown: false }} />
<Stack.Screen name="Information" component={Information} />
</Stack.Navigator>

حال می توان در یکی از این صفحات اصلی، از یکی دیگر از navigation ها استفاده کنیم:

// Home.js
<Tab.Navigator>
<Tab.Screen name="Settings" component={Settings}/>
<Tab.Screen name="Profile" component={Profile}/>
</Tab.Navigator>

ب این صورت وقتی وارد صفحه Home می شویم، در قسمت پایینی صفحه می توان Tab navigation را ببینیم.

همچنین میتوان در صفحه Settings یک دکمه قرار دهیم ک مارا ب Information ک در Stack می باشد ببرد:

// Settings.js
<Button title="Go to Information" onPress={() => navigation.navigate("Information")}/>

🎉 باید حواسمان باشد ک اگر از nested navigation استفاده کردیم، باید در نظر داشته باشیم ک history هر navigation ( دراور یا استک یا ... ) برای خودش است و تاریخچه هر تبی ک جابه جا می شویم در همان navigation ذخیره می شود پس نمی توان در navigation های دیگر ب آن یکی دسترسی داشته باشیم.

🎉 باید حواسمان باشد ک هر navigation آپشن های مختص ب خودش را دارد

🎉 همچنین ما نمی توانیم از screen استفاده شده در یک navigation به یک screen در یک navigation دیگر مقداری بفرستیم و برای حل این موضوع می توان از context یا redux استفاده کرد.

🎉 در nested navigation در واقع navigation های فرزند، رویداد های navigation والد را ارثبری نمی کنند.

🎉 همچنین UI نویگیتور والد بالای UI نویگیتور فرزند رندر می شود، به طور مثال اگر یک Drawer فرزند یک Stack باشد، هنگام باز شدن، زیر Stack باز می شود

🎉 همچنین ما میتوانیم ب صورت زیر از حالت nested استفاده کنیم و برای هر چند صفحه، یک والد در نظر بگیریم:

// App.js
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="first" options={{headerShown: false}}>
{() => (
<Stack.Navigator>
<Stack.Screen name="Settings" component={Settings}/>
<Stack.Screen name="Profile" component={Profile}/>
</Stack.Navigator>
)}
</Tab.Screen>
<Tab.Screen name="second" options={{headerShown: false}}>
{() => (
<Stack.Navigator>
<Stack.Screen name="Home" component={Home}/>
<Stack.Screen name="Information" component={Information}/>
</Stack.Navigator>
)}
</Tab.Screen>
</Tab.Navigator>
</NavigationContainer>

در این حالت history هر بخش درون همان تابع و navigation ذخیره می شود، یعنی اگر درون یک والد، به یکی از صفحات زیر مجموعه آن برویم و سپس وارد یک صفحه دیگر از یک والد دیگر شویم، هنگام برگشت باز در همان صفحه ک رفته بودیم، خواهیم رفت.

🎉 همچنین می توان درون هر کامپوننت با navigate ب هر کدام از صفحات دیگر رفت.

🎉 سعی شود ک خیلی navigation ها را nested نکنیم.

navigation life cycle#

برای اینکه هنگام هربار ورود کاربر ب یک صفحه، یک تابعی یا کدی را اجرا کنیم، چند راه وجود دارد

// Profile.js
useEffect(() => {
return navigation.addListener("focus", () => {
alert("mamad")
})
}, [navigation])

اما خود navigation این کد را پیشنهاد نمی دهد و خودش برای اینکار هوک های مجزا دارد:

import { useFocusEffect } from '@react-navigation/native';
import { useCallback } from 'react';
export default function Profile({navigation}) {
useFocusEffect(
useCallback(() => {
// Mount
alert("Focused");
// Unmount
return () => {
alert("Unfocused")
}
})
)}

fullscreen modal#

می توان توسط کد های زیر یک صفحه خاص را ب حالت مودال تمام صفحه تبدیل کرد:

<Tab.Screen name="second" options={{ headerShown: false }}>
{() => (
<Stack.Navigator>
<Stack.Group>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Information" component={Information} />
</Stack.Group>
<Stack.Group screenOptions={{ presentation: "modal" }}> {/*در واقع 3 حالت دارد ک پیش فرض آن همان کارد می باشد*/}
<Stack.Screen name="Modal" component={Modal} />
</Stack.Group>
</Stack.Navigator>
)}
</Tab.Screen>

🎉 همچنین می توان توسط Stack.Group صفحات را گروه بندی کرد و استایل های خاصی را ب هر گروه بدهیم

react-native-community/hooks@#

یکی از پکیج های عالی برای دسترسی ب موارد زیر در React Native می باشد:

@react-native-community/hooks

yarn add @react-native-community/hooks
useAccessibilityInfo
useAppState
useBackHandler
useCameraRoll => دوربین
useClipboard => کپی کردن اطلاعات
useDimensions => ابعاد گوشی
useImageDimensions
useKeyboard
useInteractionManager
useDeviceOrientation => عمودی یا افقی بودن گوشی
useLayout

useDimensions & useDeviceOrientation#

برای اینکه ابعاد گوشی و حالت افقی یا عمودی بودن آن را ب دست بیاوریم، می توان به صورت زیر از آن ها استفاده کرد:

import { useDimensions, useDeviceOrientation } from "@react-native-community/hooks";
export default function App() {
const {window, screen} = useDimensions(); // width - height
const {landscape, portrait} = useDeviceOrientation(); // true - false
return (
<View style={styles.container}>
<View style={{
width: "100%",
height: portrait ? "50%" : screen.height,
backgroundColor: "dodgerblue"}}/>
</View>
);}