دعونا نحاول رسم واجهة كاملة. نعيد كتابة App.js باستخدام المكونات من مكتبة القاعدة الأصلية:
import React from 'react'; import {Container, Content} from 'native-base'; import {StyleSheet, Text, View} from 'react-native'; import AppFooter from './components/AppFooter.js'; const styles = StyleSheet.create({ container: { padding: 20 }, }); const App = () => ( <Container> <Content> <View style={styles.container}> <Text> Lorem ipsum... </Text> </View> </Content> <AppFooter/> </Container> ); export default App;
نرى مكون AppFooter الجديد الذي يتعين علينا إنشاؤه. نذهب إلى المجلد ./components/ وننشئ ملف AppFooter.js بالمحتويات التالية:
import React from 'react'; import {Footer, FooterTab, Button, Text} from 'native-base'; const AppFooter = () => ( <Footer> <FooterTab> <Button active> <Text></Text> </Button> <Button> <Text></Text> </Button> </FooterTab> </Footer> ); export default AppFooter;
كل شيء جاهز لمحاولة بناء تطبيقنا!
أزرارنا لا تعرف حتى الآن كيفية التبديل. حان الوقت لتعليمهم. للقيام بذلك ، تحتاج إلى القيام بأمرين: تعلم كيفية التعامل مع حدث النقر ومعرفة كيفية تخزين الحالة. لنبدأ بالدولة. نظرًا لأننا رفضنا تخزين الحالة في المكون ، وبعد اختيار المكونات النقية ومتجر عالمي ، سنستخدم Redux.
بادئ ذي بدء ، يجب علينا إنشاء جانبنا.
import {createStore} from 'redux'; const initialState = {}; const store = createStore(reducers, initialState);
دعونا إنشاء فراغ للمخفضات. في مجلد المخفضات ، قم بإنشاء ملف index.js بالمحتويات التالية:
export default (state = [], action) => { switch (action.type) { default: return state } };
ربط مخفضات App.js:
import reducers from './reducers';
الآن نحن بحاجة لتوزيع متجرنا على المكونات. يتم ذلك باستخدام مكون الموفر على وجه التحديد. نربطه بالمشروع:
import {Provider} from 'react-redux';
ولف جميع المكونات في الموفر. يبدو أن App.js المحدّث مثل هذا:
import React from 'react'; import {Container, Content} from 'native-base'; import {StyleSheet, Text, View} from 'react-native'; import AppFooter from './components/AppFooter.js'; import {createStore} from 'redux'; import {Provider} from 'react-redux'; import reducers from './reducers'; const initialState = {}; const store = createStore(reducers, initialState); const styles = StyleSheet.create({ container: { padding: 20 }, }); const App = () => ( <Provider store={store}> <Container> <Content> <View style={styles.container}> <Text> Lorem ipsum... </Text> </View> </Content> <AppFooter/> </Container> </Provider> ); export default App;
الآن يمكن لتطبيقنا تخزين حالته. دعونا نستفيد من هذا. نضيف حالة الوضع ، بشكل افتراضي إلى ARTICLES. هذا يعني أنه في العرض الأول ، سيتم تعيين تطبيقنا لعرض قائمة المقالات.
const initialState = { mode: 'ARTICLES' };
ليس سيئًا ، ولكن كتابة قيم السلسلة يدويًا تؤدي إلى أخطاء محتملة. دعنا الثوابت. قم بإنشاء ملف ./constants/index.js بالمحتويات التالية:
export const MODES = { ARTICLES: 'ARTICLES', PODCAST: 'PODCAST' };
وإعادة كتابة App.js:
import {MODES} from './constants'; const initialState = { mode: MODES.ARTICLES };
حسنًا ، هناك حالة ، حان الوقت لتمريرها إلى مكون التذييل. دعنا نلقي نظرة أخرى على ./components/AppFooter.js:
import React from 'react'; import {Footer, FooterTab, Button, Text} from 'native-base'; const AppFooter = () => ( <Footer> <FooterTab> <Button active> <Text></Text> </Button> <Button> <Text></Text> </Button> </FooterTab> </Footer> ); export default AppFooter;
كما نرى ، يتم تحديد حالة المفتاح باستخدام الخاصية النشطة لمكون Button. دعنا ندفع الحالة الحالية للتطبيق إلى Button. هذا ليس صعبًا ، فالجزء الرئيسي من غطاء المحرك هو مكون المزود الذي قمنا بتوصيله مسبقًا. يبقى فقط لأخذ الحالة الحالية منه ووضع مكونات AppFooter في الخصائص (الدعائم). بادئ ذي بدء ، نقوم بتعديل AppFooter الخاص بنا بحيث يمكن التحكم في حالة الأزرار عن طريق تمرير الوضع من خلال الدعائم:
import React from 'react'; import {Footer, FooterTab, Button, Text} from 'native-base'; import {MODES} from "../constants"; const AppFooter = ({mode = MODES.ARTICLES}) => ( <Footer> <FooterTab> <Button active={mode === MODES.ARTICLES}> <Text></Text> </Button> <Button active={mode === MODES.PODCAST}> <Text></Text> </Button> </FooterTab> </Footer> ); export default AppFooter;
لنبدأ الآن في إنشاء حاوية. قم بإنشاء الملف ./containers/AppFooterContainer.js.
import React from 'react'; import AppFooter from '../components/AppFooter.js'; import {MODES} from "../constants"; const AppFooterContainer = () => ( <AppFooter mode={MODES.ARTICLES} /> ); export default AppFooterContainer;
وقم بتوصيل حاوية AppFooterContainer في App.js بدلاً من مكون AppFooter. حتى الآن ، لا تختلف حاويتنا عن المكون ، ولكن كل شيء سيتغير بمجرد توصيله بحالة التطبيق. لنفعلها!
import React from 'react'; import AppFooter from '../components/AppFooter.js'; import {connect} from 'react-redux'; const mapStateToProps = (state) => ({ mode: state.mode }); const AppFooterContainer = ({mode}) => ( <AppFooter mode={mode} /> ); export default connect( mapStateToProps )(AppFooterContainer);
وظيفية للغاية! أصبحت جميع الميزات نظيفة. ما الذي يحدث هنا؟ نقوم بتوصيل حاويتنا بالحالة باستخدام وظيفة الاتصال وربط دعائمها بمحتويات الحالة العالمية باستخدام وظيفة mapStateToProps. نظيفة جدا وجميلة.
لذا ، تعلمنا توزيع البيانات من الأعلى إلى الأسفل. الآن نحن بحاجة إلى معرفة كيفية تغيير حالتنا العالمية من الأسفل إلى الأعلى. تم تصميم الإجراءات لإنشاء أحداث حول الحاجة إلى تغيير الحالة العالمية. لنقم بإنشاء إجراء يحدث عند النقر فوق زر.
قم بإنشاء الملف ./actions/index.js:
import { SET_MODE } from './actionTypes'; export const setMode = (mode) => ({type: SET_MODE, mode});
وملف ./actions/actionTypes ، الذي سنخزن فيه الثوابت بأسماء الإجراءات:
export const SET_MODE = 'SET_MODE';
ينشئ الإجراء كائنًا باسم الحدث ومجموعة البيانات المصاحبة لهذا الحدث ، وليس أكثر. الآن سوف نتعلم كيفية إنشاء هذا الحدث. نعود إلى حاوية AppFooterContainer ونقوم بتوصيل وظيفة mapDispatchToProps التي ستربط مرسلات الأحداث بدعائم الحاوية.
import React from 'react'; import AppFooter from '../components/AppFooter.js'; import {connect} from 'react-redux'; import {setMode} from '../actions'; const mapStateToProps = (state) => ({ mode: state.mode }); const mapDispatchToProps = (dispatch) => ({ setMode(mode) { dispatch(setMode(mode)); } }); const AppFooterContainer = ({mode, setMode}) => ( <AppFooter mode={mode} setMode={setMode} /> ); export default connect( mapStateToProps, mapDispatchToProps )(AppFooterContainer);
حسنًا ، لدينا وظيفة ترفع حدث SET_MODE وتخطيناها إلى مكون AppFooter. تبقى مشكلتان:
لا أحد يدعو هذه الوظيفة.
لا أحد يستمع إلى الحدث.
سنتعامل مع المشكلة الأولى. ننتقل إلى مكون AppFooter ونربط المكالمة بوظيفة setMode.
import React from 'react'; import {Footer, FooterTab, Button, Text} from 'native-base'; import {MODES} from "../constants"; const AppFooter = ({mode = MODES.ARTICLES, setMode = () => {}}) => ( <Footer> <FooterTab> <Button active={mode === MODES.ARTICLES} onPress={ () => setMode(MODES.ARTICLES)}> <Text></Text> </Button> <Button active={mode === MODES.PODCAST} onPress={ () => setMode(MODES.PODCAST)}> <Text></Text> </Button> </FooterTab> </Footer> ); export default AppFooter;
الآن ، عند الضغط على الزر ، سيتم رفع الحدث SET_MODE. يبقى أن نتعلم كيف نغير الحالة العالمية عند ظهورها. ننتقل إلى ./reducers/index.js الذي تم إنشاؤه مسبقًا وإنشاء مخفض لهذا الحدث:
import { SET_MODE } from '../actions/actionTypes'; export default (state = [], action) => { switch (action.type) { case SET_MODE: { return Object.assign({}, state, { mode: action.mode }); } default: return state } };
عظيم! يؤدي النقر فوق الزر الآن إلى إنشاء حدث يغير الحالة العامة ، ثم يقوم التذييل ، بعد تلقي هذه التغييرات ، بإعادة رسم الأزرار.