Get in touch
or send us a question?
CONTACT

Phần 1: Làm quen với React Native qua app “Hello Neos Vietnam”

thumbnail

Xin chào,

Hôm nay chúng ta sẽ cùng nhau viết một ứng dụng đơn giản có tên là “Hello Neos Vietnam” bằng React Native. Cách hoạt động của ứng dụng là nhập một tên và hiển thị bên dưới dòng chữ “Hello [name]”

Trước tiên bạn cần cài đặt môi trường thông qua trang chủ của React Native.

Hãy chọn mục “React Native CLI Quickstart”, mục Development OS và Target OS thì tùy vào thiết bị của bạn, ở đây tôi chọn Development OS là “macOS” và Target OS là “iOS”.

Hãy kiên nhẫn làm theo từng bước cài đặt các thư viện cần thiết nhé, hành trình chỉ mới bắt đầu thôi 🙂

Sau khi cài đặt xong, sử dụng terminal, đi tới nơi bạn muốn đặt project của mình rồi cùng tạo project có tên là HelloNeosVietnam, câu lệnh bên dưới sẽ tạo ra một folder mới chứa toàn bộ source code khởi tạo ban đầu:

 npx react-native init HelloNeosVietnam

Sau khi đã tạo thành công, mở project lên và bắt đầu code nào 😉

Tôi sử dụng Visual Studio Code để code, nhưng bạn có thể tùy ý sử dụng trình soạn thảo yêu thích của mình, thậm chí cả notepad huyền thoại nhé 😉

Bên dưới là cấu trúc project:

Ban đầu có lẽ bạn chỉ cần quan tâm tới vài mục sau:

  • index.js là file đầu tiên được trỏ tới khi app khởi chạy. Nó có vai trò như file index trong dự án về web vậy.
  • package.json là nơi chứa những thư viện của dự án và config những mã lệnh như run, debug hoặc test…
  • android/ và ios/ chứa những thông tin liên quan tới từng nền tảng tương ứng.
  • App.js là file chứa một đoạn code mẫu, tuy nhiên tôi sẽ bỏ qua nó và tạo một folder của riêng mình. Vì một dự án thường sẽ có nhiều file source code cũng như image liên quan, nên tôi thường sẽ đặt chúng trong một folder để dễ dàng quản lý.

Cùng tạo ra folder có tên là “src” nằm trong project

Trong “src”, tiếp tục tạo ra file có tên là “App.js” và bắt đầu những dòng code đầu tiên thôi!

Trước tiên, ta cần thay đổi đường dẫn trong file index.js trỏ về file App.js mà ta vừa tạo ra thay vì file App.js mặc định

Mở file index.js và sửa đoạn mã thành như sau: (chú ý các đoạn mã mới sẽ được in đậm)

import {AppRegistry} from 'react-native';
import App from './src/App';
import {name as appName} from './app.json';

AppRegistry.registerComponent(appName, () => App);

Rồi, quay lại file App.js và gõ những dòng mã thân thương như sau:

import React from 'react' // 1
import { View } from 'react-native' // 2

// 3
const App = () => {
    return <View style={{ flex: 1, backgroundColor: 'green' }}>

    </View>
}

// 4
export default App

Cùng phân tích những mục ta vừa thêm

Mục (1) là lấy ra React từ thư viện ‘react’, nơi chứa những thứ cơ bản nhất của framework React Native mà chúng ta đang sử dụng. Nếu bạn muốn dựng giao diện màn hình, đừng quên React 🙂

Mục (2) là lấy ra những component ( như View, Text,… ) từ thư viện ‘react-native’

Mục (4) là export function App ở trên, mục đích để những nơi khác có thể sử dụng App (như index.js chẳng hạn)

Mục (3) là tạo ra một function có tên là App. Function này trả lại (return) một component để hiển thị lên màn hình (là View).

View là component rất cơ bản trong React, nó có thể chứa các component khác như Text, Button hoặc nó có thể chứa một hoặc nhiều View khác.

Do đó, trong mỗi màn hình, View thường được đặt ở đầu tiên, rồi bên trong nó sẽ chứa các component khác.

View hay các component khác là các đối tượng mà trong chúng có những phương thức (gọi là props) để hiển thị hoặc hành động theo cách chúng ta truyền thông số thông phù hợp.

Hãy nhớ mỗi View hoặc các component trong React đều có rất nhiều props, xem thêm tại https://reactnative.dev/docs/view

Một trong những props rất phổ biến đó là style, dùng để mô tả vị trí, hình dạng, màu sắc, .. về view đó. Cùng xem lại cách chúng ta khai báo props style trong View

<View style={{ flex: 1, backgroundColor: 'green' }}>

</View>

Ở trên, ta mô tả về View thông qua props style với flexbackgroundColor.

backgroundColor thì khá rõ ràng như tên gọi của nó rồi, còn flex thì bạn hãy nhìn hình ảnh bên dưới:

View-A (có viền đen) chứa View-B (hồng) và View-C (xanh).

View-A dựa vào mô tả flex của B và C mà chia ra các phần có tỉ lệ tương ứng. Dễ hiểu đúng không?

Nếu View-A chỉ có một ‘con’ là B hoặc C, thì dù flex bằng bao nhiêu thì A cũng dành toàn bộ những gì mình có để lại hết cho đứa con duy nhất của mình!

Quay trở lại đoạn code của chúng ta

<View style={{ flex: 1, backgroundColor: 'green' }}>

</View>

Ở đây View sẽ lấy 1 phần, hoặc tất cả (nếu nó là đứa con duy nhất) của cha nó.

Vậy nên flex:1 trong trường hợp này nghĩa là View sẽ chiếm toàn bộ không gian màn hình hiển thị của thiết bị.

Hãy cùng chạy để xem những dòng code vừa rồi sẽ hiển thị ra sao nhé!

Mở terminal, trỏ tới folder nơi chứa project (nơi chứa file index.js) và chạy dòng mã sau:

npx react-native run-ios

Hãy dành vài phút thư giãn và chờ kết quả như hình bên dưới nhé 😉

Nhờ flex:1 backgroundColor:’green’ mà ta có một màu xanh xanh như trên! 🙂

Tiếp tục nhé!

Chúng ta đã biết View là một component quan trọng của React, giờ hãy thử thêm một ứng cử viên cũng quan trọng không kém là Text nhé!

import React from 'react'
import { View, Text } from 'react-native'

const App = () => {
    return <View style={{ flex: 1 }}>
        <Text>Hello Neos Vietnam</Text>
    </View>
}
...

Tôi đã xóa bỏ backgroundColor vì tôi không thích app của mình màu mè hoa lá ;). Quan trọng là giờ trong View đã chứa một Text. Chú ý là ta cũng cần thêm Text ở mục import phía trên nhé!

Hãy cùng save file App.js và máy ảo được cập nhật giao diện rất tức thì (cảm ơn tính năng hot reload ngọt ngào mà React đã mang lại!)

Dòng chữ Hello Neos Vietnam đã khép nép ở góc trái, phía trên của màn hình.

Có vẻ như View được sinh ra trước khi màn hình khuyết tật của iPhone xuất hiện nên nó không lường trước được tình huống này. Rất may React đã có giải pháp!

Hãy thay View bởi một component khác có tên là SafeAreaView

...
import { SafeAreaView, Text } from 'react-native'

const App = () => {
    return <SafeAreaView style={{ flex: 1 }}>
        <Text>Hello Neos Vietnam</Text>
    </SafeAreaView>
}
...

Về cơ bản, SafeAreaViewem của View, nhưng nó sẽ đảm bảo các con của nó sẽ không bị thiệt thòi bởi màn hình khuyết tật như trào lưu mà iPhone khởi sướng.

Hãy lưu file và xem lại màn hình hiển thị:

OK rồi!

Giờ, hãy thêm chút style để Text trở nên fashion nhé!

...
const App = () => {
    return <SafeAreaView style={{ flex: 1 }}>
        <Text style={{
            fontSize: 20,
            fontWeight: 'bold',
            alignSelf: 'center'
        }}>NEOS VIETNAM</Text>
    </SafeAreaView>
}
...

Chú ý là tôi đổi text từ “Hello Neos Vietnam” trở thành “NEOS VIETNAM”, cũng như thêm style liên quan tới Text. Và đặc biệt phải luôn nhớ rằng nếu bạn muốn biết một component có những style gì cũng như props nào khác thì ghé thăm trang chủ nhé! Chọn một component bạn hứng thú và nhìn sang phía bên phải của màn hình, ở đó sẽ có tất cả những props mà component đó cung cấp.

Nhớ nhé! Trang chủ của framework luôn là nơi tình yêu bắt đầu!

Đây là màn hình sau khi ta đã thay đổi code (nhớ save nhé :))

Tiếp theo, hãy đặt NEOS VIETNAM vào trong một View. Mục đích là để có thể style view đó theo thiết kế dễ dàng hơn.

...
return <SafeAreaView style={{ flex: 1 }}>
        <View style={{
            height: 50,
            backgroundColor: 'red',
            justifyContent: 'center',
            alignItems: 'center'
        }}>
            <Text style={{
                fontSize: 20,
                fontWeight: 'bold'
            }}>NEOS VIETNAM</Text>
        </View>

</SafeAreaView>
...

Ta bỏ đi trường alignSelf: ‘center’ của Text vì nó không cần thiết nữa.

View mới được thêm có 2 trường ta cần hiểu là justifyContentalignItems.

Nhớ rằng View có 2 cách sắp xếp các con của nó, theo chiều dọc (mặc định) và ngang. Với justifyContent, nó sẽ ảnh hưởng theo chiều mà View đó đang sắp xếp. Ví như View sắp xếp con của nó theo chiều dọc thì justifyContent: ‘center’ sẽ khiến các con của nó ở giữa của View theo chiều dọc. Còn alignItems sẽ xếp con của nó theo chiều ngược với justifyContent, nghĩa là nếu justifyContent xếp theo chiều dọc thì alignItems theo chiều ngang và ngược lại.

Tóm lại, với việc set cùng lúc 2 trường như ở trên thì Text sẽ nằm ở giữa của View. Right?!

Save & see it!

Dải màu đỏ chính là View mà chúng đã vừa thêm. Nó chứa Text (NEOS VIETNAM) ở chính giữa của mình.

Giờ hãy bỏ đi background đỏ và thêm vào chút đổ bóng cho View như đoạn mã dưới đây:

...
        <View style={{
            height: 50,
            backgroundColor: 'white',
            justifyContent: 'center',
            alignItems: 'center',
            shadowColor: 'black',
            shadowOpacity: 0.1,
            shadowOffset: {
                height: 4
            }
        }}>
            <Text style={{
                fontSize: 18,
                fontWeight: 'bold'
            }}>NEOS VIETNAM</Text>
        </View>
...

Tôi thay đổi chút fontSize của Text và thêm chút shadow cho View. Trông rất ok đấy chứ?

Tiếp theo hãy thêm một component làm nhiệm vụ nhập text của người dùng tên là TextInput.

...
import { SafeAreaView, Text, TextInput, View } from 'react-native'

const App = () => {
    return <SafeAreaView style={{ flex: 1 }}>
        <View style={{
            height: 50,
            backgroundColor: 'white',
            justifyContent: 'center',
            alignItems: 'center',
            shadowColor: 'black',
            shadowOpacity: 0.1,
            shadowOffset: {
                height: 4
            }
        }}>
            <Text style={{
                fontSize: 18,
                fontWeight: 'bold'
            }}>NEOS VIETNAM</Text>
        </View>

        <TextInput
            style={{
                marginTop: 30,
                marginHorizontal: 20,
                height: 50,
                borderWidth: 1,
                borderColor: '#E3E3E3',
                borderRadius: 4
            }}
        />

    </SafeAreaView>
}
...

Cũng như View và Text thì TextInput cũng có props kinh điển là style với những trường rất dễ hiểu như chính tên của nó mô tả.

marginTop: 30 là khoảng cách 30 của TextInput so với component ngay phía trên

marginHorizontal: 20 là khoảng cách 2 bên trái-phải

Nhớ import TextInput ở trên đầu nữa nhé!

Giờ hãy thêm một props có tên là placeholder ở TextInput

        <TextInput
            style={{
                marginTop: 30,
                marginHorizontal: 20,
                height: 50,
                borderWidth: 1,
                borderColor: '#E3E3E3',
                borderRadius: 4
            }}
            placeholder='type a name here'
        />

props này có tác dụng hiển thị text có nội dung là ‘type a name here’ ở TextInput khi user chưa gõ gì

Thêm chút textAlign: ‘center’fontSize: 16 để text được đặt ở giữa và font chữ lớn hơn chút so với mặc định. Thêm props có tên là autoCorrect={false} để ngăn TextInput tự động sửa lỗi khi user gõ tên.

...
            style={{
                marginTop: 30,
                marginHorizontal: 20,
                height: 50,
                borderWidth: 1,
                borderColor: '#E3E3E3',
                borderRadius: 4,
                textAlign: 'center',
                fontSize: 16
            }}
            autoCorrect={false}
...

Để có thể lấy thông tin của TextInput, ta sử dụng một props có tên là onChangeText, dưới đây là ảnh tôi đã chụp ở trang chủ của React Native (một lần nữa tôi luôn muốn nhắn nhủ với các bạn, hãy nhớ đến trang chủ của React Native, đó là nơi tình yêu bắt đầu, xin hãy trân trọng!)

Hãy thêm onChangeText vào TextInput như bên dưới:

...
        <TextInput
            style={{...}}
            autoCorrect={false}
            placeholder='type a name here'
            onChangeText={(text) => {
                console.log(text)
            }}
        />
...

onChangeText là một function có 1 tham số kiểu string, tên là text, chính là chuỗi kí tự mà TextInput sẽ trả lại mỗi khi ta gõ gì đó.

console.log(text) để in ra chuỗi kí tự text, hãy nhìn cửa sổ terminal, mỗi khi chúng ta gõ một kí tự nào đó vào TextInput (hãy nhớ là project phải được được khởi chạy và có ít nhất 2 cửa sổ terminal đang được mở, một cửa sổ ta chạy dòng mã npx react-native run-ios và cửa sổ còn lại sẽ hiển thị thông tin, trong đó có những thông tin từ console.log() như trên)

Khi tôi gõ xong chữ “Ha van thanh” như ảnh bên dưới:

Thì đây là những gì tôi nhận được ở cửa sổ terminal:

Như vậy là rõ ràng, hàm onChangeText sẽ luôn được gọi mỗi khi user nhập kí tự nào đó vào TextInput.

Ta sẽ lưu lại dữ liệu mà hàm onChangeText trả về vào một biến có tên là name như đoạn mã bên dưới

const App = () => {
    let name = ''

    return <SafeAreaView style={{ flex: 1 }}>
        <View style={{...}}>
            <Text style={...}}>NEOS VIETNAM</Text>
        </View>

        <TextInput
            style={{...}}
            placeholder='type a name here'
            onChangeText={(text) => {
                // console.log(text)
                name = text
            }}
        />

    </SafeAreaView>
}

Về cơ bản, trong React Native có 2 cách để khai báo 1 biến, là constlet. Trong khi const dành cho những biến không thay đổi giá trị đúng như tên gọi của nó, thì let cho phép giá trị thay đổi như biến name ở trên được thay đổi mỗi khi onChangeText được gọi tới (khi user nhập kí tự vào TextInput)

Sau khi đã lưu trữ dữ liệu thành công vào biến name, việc tiếp theo, chúng ta sẽ tạo ra một component tên là Text, nằm phía dưới TextInput, nhằm hiển thị dòng chữ “Hello {name}” (nhớ rằng name chính là biến chứa dữ liệu từ hàm onChangeText ở phía trên, nếu name là “Ha Van Thanh” thì Text sẽ hiển thị là “Hello Ha Van Thanh”, right? 🙂

Chúng ta sẽ thay đổi chút, đặt giá trị ban đầu cho name là “Thanh”, và khai báo Text như sau:

...
const App = () => {
    let name = 'Thanh'

    return <SafeAreaView style={{ flex: 1 }}>
        <View style={{...}}>
            <Text style={{...}}>NEOS VIETNAM</Text>
        </View>

        <TextInput .../>

        <Text style={{
            marginTop: 20,
            alignSelf: 'center',
            fontSize: 18
        }}>Hello {name}</Text>

    </SafeAreaView>
}
...

Khi đó, màn hình ban đầu của app sẽ hiển thị như sau:

Tuy nhiên, khi user gõ kí tự vào TextInput, dù name đã được gán giá trị mới ở hàm onChangeText, nhưng Text thì không thay đổi theo. Tại sao?

Đó là bởi vì, Text sẽ không được biết khi nào name được thay đổi giá trị để Text biết mà hiển thị lại.

Do đó, cần phải có một cách nào đó để mỗi khi name thay đổi, nó sẽ thông báo kiểu như: “Hey, tôi đã thay đổi, ai đó (component như Text) sử dụng tôi thì hãy thay đổi theo đi. Cảm ơn!”.

Rất tuyệt vời là React Native có hỗ trợ điều này. React gọi các biến đó là state. Hãy nhớ, state là một kiểu biến mà mỗi khi nó thay đổi, nó sẽ loa loa để tất cả các component sử dụng nó biết mà cập nhật lại giao diện cho phù hợp với dữ liệu mới.

Hãy thay đổi cách khai báo name theo dạng state như sau:

import React, { useState } from 'react'
import { SafeAreaView, Text, TextInput, View } from 'react-native'

const App = () => {
    let name = 'Thanh'
    const [name, setName] = useState('Thanh')

Chúng ta sử dụng useState – một function của framework nhằm tạo ra một state. Tham số truyền vào của useState là giá trị mặc định (trường hợp trên là chuỗi kí tự “Thanh”). Giá trị trả về của useState là một mảng có 2 phần tử, tương ứng là biến name mà chúng ta sẽ sử dụng, và setName là một function để set giá trị mới của name.

Trong hàm onChangeText của TextInput, chúng ta cũng thay đổi cách gán giá trị mới cho name bằng setName, như đoạn mã sau:

...
        <TextInput
            style={{...}}
            autoCorrect={false}
            placeholder='type a name here'
            onChangeText={(text) => {
                name = text
                setName(text)
            }}
        />
...

Việc này sẽ giúp hệ thống biết được rằng name đã thay đổi và các component sử dụng name sẽ cần thay đổi, cần hiển thị lại (re-render hoặc update new interface)

Sau khi lưu lại, hãy cùng trở lại màn hình máy ảo, gõ gì đó vào TextInput và cùng xem:

Perfect! 🙂

Tóm lại, mỗi khi ta thay đổi các kí tự trong TextInput, hàm onChangeText hoạt động, trong hàm đó, sử dụng setName của useState để khiến state name thay đổi, khi name thay đổi, những component sử dụng nó (ở đây chỉ có component Text) sẽ thay đổi theo.

Tổng kết lại, hãy nhớ:

  • React Native có View, Text, TextInput và rất nhiều các component khác để hiển thị UI. Hãy nhớ vào link sau mỗi khi cần dùng một component nào đó nhé https://reactnative.dev/docs/components-and-apis
  • style là một props rất phổ biến nhằm định nghĩa cách component hiển thị trên màn hình. Ngoài ra, mỗi component lại có những props riêng, như onChangeText của TextInput chẳng hạn.
  • Trong khi props định nghĩa những giá trị tĩnh của component, thì state lại được sinh ra để định nghĩa những giá trị động. Khi state thay đổi, những component liên quan sẽ thay đổi theo nó.
  • React Native rất tuyệt vời! Bài này chỉ là những bước chân đầu tiên! Hãy nhớ trang chủ của RN tại https://reactnative.dev

Xin kết thúc phần 1 tại đây. Hi vọng những phần sau, chúng ta sẽ cùng nâng cấp app này hơn nữa, thông qua đó cũng mong rằng các bạn sẽ yêu React Native hơn :).

Thanks/

Leave a Reply

Your email address will not be published. Required fields are marked *