Lập trình ReactNative – Học lập trình, học sử dụng máy tính từ số 0 – ZeZo.dev https://zezo.dev Sat, 30 Nov 2024 11:16:27 +0000 vi hourly 1 https://wordpress.org/?v=6.7.1 https://zezo.dev/wp-content/uploads/2024/11/cropped-zzel-32x32.png Lập trình ReactNative – Học lập trình, học sử dụng máy tính từ số 0 – ZeZo.dev https://zezo.dev 32 32 Hiệu ứng chuyển màn hình trong ứng dụng ReactNative https://zezo.dev/view/hieu-ung-chuyen-man-hinh-trong-ung-dung-reactnative Sat, 30 Nov 2024 11:12:14 +0000 https://zezo.dev/?p=331 Ứng dụng viết bằng ReactNative sử dụng thư viện react-native-navigation để điều hướng các màn hình. Thư viện này có tích hợp sẵn animation giúp chuyển đổi màn hình có hiệu ứng đẹp. Để thực hành demo, ứng dụng mới tạo cần có navigation https://reactnavigation.org/ và có 2 component screen.

Các bước thực hiện:

Bước 1: Sau khi có project, tiến hành cài thư viện navigation

npm install @react-navigation/native 
npm install react-native-screens react-native-safe-area-context
npm install @react-navigation/stack 
npm install --save react-native-gesture-handler

Xem chi tiết tại https://reactnavigation.org/docs/stack-navigator

Bước 2: Tạo component và code chuyển màn hình

Trong phần ví dụ này sẽ làm việc trên 1 file App.tsx


import { Button, StyleSheet, Text, View } from 'react-native'
import React from 'react'
import { createStackNavigator } from '@react-navigation/stack' // bỏ chữ Native
import { NavigationContainer } from '@react-navigation/native'


const Screen1 = (props)=>{
 return (
   <View style={{ flex:1, backgroundColor: 'yellow'}}>
     <Text>Screen 11111 </Text>
     <Button title='Sang màn 2'
     onPress={()=>props.navigation.navigate('Screen2')}/>
   </View>
 )
}
const Screen2 = ()=>{
 return (
   <View style={{ flex:1, backgroundColor: 'cyan'}}>
     <Text>Screen 2222 </Text>
   </View>
 )
}

const StackDemo = createStackNavigator(); 


const App = () => {
 return (
   <NavigationContainer>
     <StackDemo.Navigator initialRouteName='Screen1'
       screenOptions={ { headerShown: false}}
     >
       <StackDemo.Screen name='Screen1' component={Screen1} />
       <StackDemo.Screen name='Screen2' component={Screen2} />


     </StackDemo.Navigator>
   </NavigationContainer>
 )
}


export default App

const styles = StyleSheet.create({})

Bước 3: Chạy ứng dụng để kiểm tra việc chuyển đổi màn hình

Sử dụng lệnh:

npm start -- --reset-cache

Bước 4: Cải tiến ứng dụng thêm hiệu ứng

Sau khi chạy được ứng dụng thành công thì cải tiến thêm hiệu ứng chuyển màn hình

<StackDemo.Navigator initialRouteName='Screen1'
      screenOptions={ { headerShown: true,
       cardStyleInterpolator: ( {current, layouts }) => (
         {
           cardStyle:{
             // các thuộc tính hiệu ứng viết ở đây:
             opacity: current.progress,//thay đổi độ trong suốt theo quá trình hiển thị
             transform: [
               {
                  translateX: current.progress.interpolate({
                   inputRange:[0,1],
                   outputRange:[layouts.screen.width, 0]
                  })
               },
               {
                 scale: current.progress.interpolate({
                   inputRange:[0,1],
                   outputRange:[0.5,1]
                 })
               }
              
             ]
           }
         }
       )

      }}
    >

Chú ý không quên chạy lại lệnh như ở bước 3

 

Tham khảo thêm các hiệu ứng tại https://reactnavigation.org/docs/shared-element-transitions/

 

 

]]>
code demo tạo chức năng login trong reactnative với json server API https://zezo.dev/view/code-demo-tao-chuc-nang-login-trong-reactnative-voi-json-server-api Fri, 29 Nov 2024 17:27:08 +0000 https://zezo.dev/?p=273

//1. Tạo navigation tạo ra 2 màn hình: Login và Home, khi vào app thì hiển thị login, đúng thông tin thì tự chuyển sang home


//======== File App.js ====================================
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import LoginComp from './comp/LoginComp';
import HomeComp from './comp/HomeComp';
const StackDemo = createNativeStackNavigator();


const App = () => {
 return (
   // Bước 3: Định nghĩa Navigation
   //Tình huống 1: App chính chỉ có Nav
   <NavigationContainer>
     <StackDemo.Navigator initialRouteName='LoginComp'>
       <StackDemo.Screen name='HomeComp' component={HomeComp} options={{ title: 'Trang chủ' }} />
       <StackDemo.Screen name='LoginComp' component={LoginComp}
                       options={{headerShown: false}} />
  
     </StackDemo.Navigator>
   </NavigationContainer>


 )
}


export default App;

//======== File LoginComp.js ====================================

import { View, Text, Button } from "react-native";
import { TextInput } from "react-native-gesture-handler";
import { useState } from "react";
import AsyncStorage from '@react-native-async-storage/async-storage';
const LoginComp =( props )=>{
   const [username, setusername] = useState("");
   const [passwd, setpasswd] = useState("");
   const doLogin = ()=>{
       // kiểm tra hợp lệ dữ liệu
       if(username.length == 0) {
           alert("Chưa nhập Username"); return;
       }
       if(passwd.length == 0) {
           alert("Chưa nhập Password"); return; // lệnh return để thoát hàm login
       }
 
       // thực hiện fetch để lấy dữ liệu về
       let url_check_login = "http://192.168.75.154:3000/tb_users?username=" + username;


       fetch (url_check_login)
       .then ( (res)=>{ return res.json (); } )
       .then (   async  (res_login) => {  
           if(res_login.length != 1)
           {
               alert("Sai username hoặc lỗi trùng lặp dữ liệu");
               return;
           }else{
               // số lượng lấy được 1 bản ghi ==> kiểm tra password
               let objU = res_login[0];
               if(objU.passwd != passwd){
                   alert("Sai password"); return;
               }else{
                   // đúng password: lưu thông tin vào storage
                   try {
                       await AsyncStorage.setItem('loginInfo',  JSON.stringify( objU )   );
                       // chuyển màn hình sang màn hình home
                       props.navigation.navigate('HomeComp');


                     } catch (e) {
                       // saving error
                       console.log(e);
                     }
               }


           }


       })
 

   }
   return (
       <View style={{ margin:30}} >
           <Text>Màn hình đăng nhập</Text>
           <TextInput placeholder="Username" onChangeText={  (txt)=>{ setusername(txt)} } />
           <TextInput placeholder="Passwd" onChangeText={  (txt)=>{ setpasswd(txt)} }
                   textContentType="password" secureTextEntry={true} />
           <Button title="Login" onPress={doLogin} />


       </View>
   )
}


export default LoginComp;

 
//======== File HomeComp.js ====================================

import { View, Text } from "react-native";
import AsyncStorage from '@react-native-async-storage/async-storage';
import React,{ useState } from "react";


const HomeComp =(props)=>{
   const [loginInfo, setloginInfo] = useState({})
   const getLoginInfo = async()=>{
       try {
           const value = await AsyncStorage.getItem('loginInfo')
           if(value !== null) {
               // lấy được dữ liệu
               setloginInfo (   JSON.parse (value)  );


           }
         } catch(e) {
           // error reading value
           console.log(e);
         }
   }


   React.useEffect(() => {
       const unsubscribe = props.navigation.addListener('focus', () => {
           // khi màn hình được active thì lệnh trong này hoạt động
          getLoginInfo();
       });
  
       return unsubscribe;
     }, [props.navigation]);

   return (
       <View>
           <Text>Màn hình Home</Text>
           <Text>Username:  {loginInfo.username} -- {loginInfo.fullname} </Text>
       </View>
   )
}
export default HomeComp;
]]>
Code mẫu sử dụng Flatlist trong React native https://zezo.dev/view/code-mau-su-dung-flatlist-trong-react-native Fri, 29 Nov 2024 17:23:25 +0000 https://zezo.dev/?p=265 Trong ví dụ này sẽ sử dụng dữ liệu ở trong một biến mảng cục bộ, khi có tương tác API thì có thể thay thế mảng bằng dữ liệu lấy từ API về là hiển thị được.

Các component được import vào ứng dụng:

import React from 'react';
import {
  SafeAreaView,
  View,
  FlatList,
  StyleSheet,
  Text,
  StatusBar,
} from 'react-native';

Bước 1: Tạo dữ liệu nguồn

 

const DATA = [
  {
    id: '1',
    ten_sp: 'First Item',
  },
  {
    id: '2',
    ten_sp: 'Second Item',
  },
  {
    id: '3',
    ten_sp: 'Third Item',
  } 
];

Sau này lấy dữ liệu từ API về chỉ cần thay thế vào biến DATA và tùy chỉnh hiển thị cho từng dòng là được.

 

Bước 2: Tạo component để hiển thị dòng

const Item = ({sp}) => (
  <View style={styles.item}>
        <Text style={styles.title}>{sp.ten_sp}</Text>
        <Text style={styles.title}>{sp.id}</Text>
  </View>
);

Component này dùng để tùy chỉnh nội dung cho từng dòng. Nếu có tương tác gì với từng dòng thì viết code ở trong component này.

Bước 3: Trong hàm App sử dụng component Flatlist

 

const App = () => {
  return (
    <SafeAreaView style={styles.container}>
      <FlatList
        data={DATA}
        renderItem={({item}) => <Item sp={item} />}
        keyExtractor={item => item.id}
      />
    </SafeAreaView>
  );
};
export default App;

Trong thẻ FlatList cần chú ý phải có tối thiểu 3 thuộc tính: data, renderItem, keyExtractor

– data: là danh sách dữ liệu

– renderItem: Chỉ định component sẽ trình bày từng dòng

– keyExtractor: chỉ định một giá trị dùng để phân biệt giữa các dòng, thông thường là ID của phần tử trong danh sách DATA

Bước 4: Định nghĩa style

const styles = StyleSheet.create({
  container: {
    flex: 1,
    marginTop: StatusBar.currentHeight || 0,
  },
  item: {
    backgroundColor: '#f9c2ff',
    padding: 20,
    marginVertical: 8,
    marginHorizontal: 16,
  },
  title: {
    fontSize: 32,
  },
});

 

]]>
Code mẫu sử dụng state trong component React native https://zezo.dev/view/code-mau-su-dung-state-trong-component-react-native Fri, 29 Nov 2024 17:22:21 +0000 https://zezo.dev/?p=262

Trong ví dụ này sẽ sử dụng một state để lưu trữ một chỗi và thực hiện tương tác với chuỗi.

Bạn tạo nội dung mới cho file App.tsx để thực hành, trước khi tạo nên backup nội dung đã có. Nếu bạn tạo project mới thì bạn xóa nội dung trong file App.tsx đi để viết component mới .

Bước 1: Import thư viện React và hook useState

 

import React ,{useState} from 'react'; 
// chú ý State nằm trong thư viện react không phải react-native

Bước 2: Trong component App, khai báo state

const [hoTen, setHoTen] = useState("Nguyen Van A");
// giá trị mặc định khi khởi tạo sẽ là "Nguyen Van A", nếu bạn không muốn khởi tạo mặc định thì để chuỗi rỗng vào đó
//const [hoTen, setHoTen] = useState("");

Bước 3: Trên View in giá trị state ra màn hình

 

return (
	<View>
		<Text>Họ tên: {hoTen}</Text>
	</View>
	);

Chạy thử ứng dụng để xem có hiển thị họ tên lên màn hình không. Nếu có thì là sử dụng state thành công.

 

Bước 4: Cải tiến thêm tương tác với state

Tình huống sử dụng một ô nhập text, người dùng nhập sẽ cập nhật vào state và hiển thị từ state lên màn hình.

Bạn viết têm thẻ TextInput vào phần return ở dưới thẻ text họ tên

 

return (
	<View>
		<Text>Họ tên: {hoTen}</Text>
		<TextInput placeholder="Nhập họ tên" onChangeText = {CapNhat} />
	</View>
	);

Trong code trên chú ý thuộc tính onChangeText  khác với thuộc tính onChange nên bạn cần làm đúng thuộc tính.

Viết hàm CapNhat ở trước lệnh return

const CapNhat = (dulieu)=>{
		setHoTen(dulieu);
}

setHoTen là hàm gán dữ liệu cho biến hoTen của useState. Biến hoTen là một biến chỉ đọc, không gán được dữ liệu trực tiếp nên phải dùng hàm setHoTen.

==>Chạy ứng dụng và thử nghiệm nhập nội dung vào ô input để quan sát thay đổi trên màn hình.

Bước 5: Cải tiến thay đổi dữ liệu khi bấm nút

Tạo một nút bấm, khi bấm vào thì sẽ chuyển nội dung trong biến thành chữ in hoa. Khi đổi giá trị trong state thì giá trị sẽ tự động hiển thị lên màn hình.

Viết hàm ChuyenChuHoa ở trước lệnh return

const ChuyenChuHoa = ()=>{
	setHoTen (  hoTen.toUpperCase() );
}

Trong thẻ View, viết thêm nút bấm

<Button title="Chuyển thành chữ hoa" onPress={ChuyenChuHoa} />

Sự kiện onPress của nút bấm sẽ gọi hàm ChuyenChuHoa ra thực thi.

 

==> chạy ứng dụng và bấm nút để xem kết quả.

 

Dưới đây là code đầy đủ của ứng dụng

Code mẫu sử dụng state trong reactnative

]]>
Cải tiến ứng dụng todo làm chức năng đổi trạng thái todo với redux https://zezo.dev/view/cai-tien-ung-dung-todo-lam-chuc-nang-doi-trang-thai-todo-voi-redux Fri, 29 Nov 2024 17:16:54 +0000 https://zezo.dev/?p=256

Nối tiếp bài viết https://zezo.dev/view/cai-tien-ung-dung-todo-lam-chuc-nang-sua-voi-redux

Tạo hàm trong reducer

,   toggleTodoStatus(state, action) {
          // tìm các todo, nếu cái nào phù hợp thì cập nhật trạng thái
          const todo = state.listTodo.find(row => row.id === action.payload);
          if (todo) {
            todo.status = !todo.status;
          }
        },

Sang TodoScreen thêm hàm xử lý action


  const handleToggleTodo = id => {
      dispatch(toggleTodoStatus(id));
  };

Sửa phần hiển thị danh sách thêm nút bấm trạng thái:


<TouchableOpacity onPress={() => handleToggleTodo(row.id)}>
              {row.status ?
                 <Text style={{ color: 'gray' }}>Completed</Text> :
                    <Text style={{ color: 'green' }}>Working</Text>
               }
</TouchableOpacity>

Xem ảnh vị trí đặt trạng thái

vị trí đặt code trạng thái ứng dụng todo

]]>
Cải tiến ứng dụng todo làm chức năng Sửa với redux https://zezo.dev/view/cai-tien-ung-dung-todo-lam-chuc-nang-sua-voi-redux Fri, 29 Nov 2024 17:15:07 +0000 https://zezo.dev/?p=253 Nối tiếp bài viết về chức năng xóa https://zezo.dev/view/cai-tien-ung-dung-todo-lam-chuc-nang-xoa-voi-redux

Tác động đến reducer để tạo hàm sửa, cải tiến phần giao diện

Vào file todoReducer thêm hàm updateTodo

updateTodo (state, action){
           // lấy tham số truyền vào
           const {id, title} = action.payload;
           // tìm bản ghi phù hợp với tham số truyền vào
           const todo = state.listTodo.find(row => row.id === id);
           // update
           if(todo){
               todo.title = title; // gán giá trị
           }
}

làm chức năng sửa  trong redux

Cải tiến giao diện

Khai báo state để xác định bản ghi nào cần sửa

// Dành cho sửa: Cần có state lưu trạng thái đang sửa bản ghi nào
  const [editTitle, setEditTitle] = useState('');// chuỗi tiêu đề
  const [idEdit, setIdEdit] = useState(null); //lưu id bản ghi cần sửa

Viết các hàm tương tác sửa

const handleEdit = (id, title) =>{
      // hàm này để  ẩn hiện form sửa
      setEditTitle(title);
      setIdEdit(id);
  }
  // hàm lưu kết quả sửa
  const handleUpdate =()=>{
      if(editTitle.trim() !== ''){
          // có nội dung sửa
          dispatch( updateTodo ({id: idEdit, title: editTitle }) );
          setEditTitle('');
          setIdEdit(null);
      }
  }

Cải tiến phần return

return (
      <View>
          <TextInput placeholder="Nhập công việc" onChangeText={setTitle} />
          <View style={{width:100}}>
              <Button title="Thêm việc" onPress={handleAddTodo} />
          </View>
          {/* in danh sách todo: */}
          {
              listTodo.map(row =>(
                <View key={row.id}
                 style={{margin:10,padding:10, borderColor:'blue', borderWidth:1}}>
                 
                  {
                      (idEdit === row.id)?
                          (<>
                              <TextInput
                                      value={editTitle}
                                      onChangeText={setEditTitle}
                                      onBlur={handleUpdate}
                                      autoFocus
                                  />
                                  <Button title="Update" onPress={handleUpdate} />
                          </>
                          )
                      :
                          (
                              <>
                                <Text>{row.title}  -  {row.id}</Text>
                                  <TouchableOpacity onPress={()=>handleDeleteTodo(row.id)} >
                                      <Text style={{color: 'red'}}>Xóa</Text>
                                  </TouchableOpacity>
<TouchableOpacity onPress={() => handleEdit(row.id, row.title)}>
                                      <Text>Edit</Text>
                                  </TouchableOpacity>

                              </>

                          )
                  }
                 
                </View> 
              ))
          }
      </View>
  );

Chạy lại ứng dụng và thử nghiệm chức năng sửa

]]>
Tạo ứng dụng Todo với Redux có tương tác API trong Reactnative – P5 https://zezo.dev/view/tao-ung-dung-todo-voi-redux-co-tuong-tac-api-trong-reactnative-p5 Fri, 29 Nov 2024 17:11:09 +0000 https://zezo.dev/?p=249 Tiếp theo bài này, ít nhất bạn cần thực hiện thành công ví dụ mẫu ở phần 1: https://zezo.dev/view/tao-ung-dung-todo-voi-redux-co-tuong-tac-api-trong-reactnative-p1

Trong phần này sẽ thực hiện xây dựng chức năng Đổi trạng thái

Bước 1: Chỉnh sửa Reducer thêm thao tác xử lý Đổi trạng thái

Bên trong hàm builder của extraReducers, bạn thêm đoạn code xử lý kết Thêm toggleTodoApi.fulfilled

         builder.addCase(toggleTodoApi.fulfilled, (state, action)=>{
              // lấy tham số truyền vào
              // console.log(action);
              const { id,  status } = action.payload;
              // tìm bản ghi phù hợp với tham số truyền vào
              const todo = state.listTodo.find(row => row.id === id);
              // update
              
              if (todo ) {
                todo.status = status; // gán giá trị
              }
            
            })
            .addCase(toggleTodoApi.rejected, (state, action) => {
              // Xử lý khi yêu cầu Sửa todo bị từ chối hoặc xảy ra lỗi
              console.log('Update todo rejected:', action.error.message);
            });

redux api phần 5

Bước 2: Tạo hàm toggleTodoApi cho todoAction.js

Bạn vào file todoAction.js thêm hàm toggleTodoApi như dưới đây. Chuỗi ‘todo/toggleTodoApi’ là định danh xác định tên action.

export const toggleTodoApi = createAsyncThunk(
    'todo/toggleTodoApi',
    async (objUpdate, thunkAPI) => {
      // console.log('objupdate: '+ JSON.stringify(objUpdate));
       try {
        // Gửi yêu cầu update đến API  
        const response = await fetch(`${api_url}/${objUpdate.id}`, {
          method: 'PUT',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(objUpdate.data)
        });
        
        const data = await response.json();
        // console.log(response);
        // Kiểm tra nếu status code là 200 hoặc 204 thì xóa thành công
        if (response.ok) {
            // console.log(response);
          // Sau khi xóa thành công, trả về id của todo đã xóa để cập nhật store
           return data; 
        } else {
          // Nếu có lỗi từ phía server, trả về lỗi
          const errorData = await response.json();
          return thunkAPI.rejectWithValue(errorData);
        }
      } catch (error) {
        // Xử lý lỗi nếu có bất kỳ lỗi nào xảy ra
        return thunkAPI.rejectWithValue(error.message);
      }
    }
  );

 

 Bước 3: Chỉnh sửa todoScreen.js để hiển thị chức năng Đổi trạng thái

Trước lệnh return của component TodoScreen, bạn thêm hàm xử lý SỬA như sau:

 

 const handleToggleTodo = (id,status) => {
        // dispatch(toggleTodoStatus(id));
        console.log('status: ' + status);
        let duLieuUpdate = {status: !status}; 
        console.log(duLieuUpdate);
        dispatch(toggleTodoApi({id: id, data:duLieuUpdate}))
        .then((result) => {
            // console.log(JSON.stringify(result));
            console.log('Todo update status successfully!');
           
        })
        .catch((error) => {
            console.error('Error update todo:', error);
        });
    }; 

Không quên import toggleTodoApi

import { fetchTodos, deleteTodoApi, addTodoAPI, updateTodoApi,toggleTodoApi } from '../redux/actions/todoAction';

Trong phần return bạn cần thêm vào chỗ hiển thị dòng dữ liệu một nút bấm

 

 <TouchableOpacity onPress={() => handleToggleTodo(row.id, row.status )}>
       {row.status ?
           <Text style={{ color: 'gray' }}>Completed</Text> :
           <Text style={{ color: 'green' }}>Working</Text>
       }
 </TouchableOpacity>

 

redux api p5

Chạy lại ứng dụng để thử nghiệm kết quả.

 

]]>
Tạo ứng dụng Todo với Redux có tương tác API trong Reactnative – P4 https://zezo.dev/view/tao-ung-dung-todo-voi-redux-co-tuong-tac-api-trong-reactnative-p4 Fri, 29 Nov 2024 17:07:14 +0000 https://zezo.dev/?p=246 Tiếp theo bài này, ít nhất bạn cần thực hiện thành công ví dụ mẫu ở phần 1: https://zezo.dev/view/tao-ung-dung-todo-voi-redux-co-tuong-tac-api-trong-reactnative-p1

Trong phần này sẽ thực hiện xây dựng chức năng Sửa

Bước 1: Chỉnh sửa Reducer thêm thao tác xử lý SỬA

Bên trong hàm builder của extraReducers, bạn thêm đoạn code xử lý kết Thêm updateTodoApi.fulfilled

builder.addCase(updateTodoApi.fulfilled, (state, action)=>{
            // lấy tham số truyền vào
            // console.log(action);
            const { id, title } = action.payload;
            // tìm bản ghi phù hợp với tham số truyền vào
            const todo = state.listTodo.find(row => row.id === id);
            // update
            if (todo ) {
                todo.title = title; // gán giá trị
            }
      })
      .addCase(updateTodoApi.rejected, (state, action) => {
            // Xử lý khi yêu cầu Sửa todo bị từ chối hoặc xảy ra lỗi
            console.log('Update todo rejected:', action.error.message);
});

redux api p4

Bước 2: Tạo hàm updateTodoApi cho todoAction.js

Bạn vào file todoAction.js thêm hàm updateTodoApi như dưới đây. Chuỗi ‘todo/updateTodoApi’ là định danh xác định tên action.

export const updateTodoApi = createAsyncThunk(
  'todo/updateTodoApi',
  async (objUpdate, thunkAPI) => {
    // console.log('objupdate: '+ JSON.stringify(objUpdate));
     try {
      // Gửi yêu cầu DELETE đến API để xóa todo

      const response = await fetch(`${api_url}/${objUpdate.id}`, {
        method: 'PUT',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(objUpdate.data)
      });
      
      const data = await response.json();
      // console.log(response);
      // Kiểm tra nếu status code là 200 hoặc 204 thì xóa thành công
      if (response.ok) {
          // console.log(response);
        // Sau khi xóa thành công, trả về id của todo đã xóa để cập nhật store
         return data; 
      } else {
        // Nếu có lỗi từ phía server, trả về lỗi
        const errorData = await response.json();
        return thunkAPI.rejectWithValue(errorData);
      }
    } catch (error) {
      // Xử lý lỗi nếu có bất kỳ lỗi nào xảy ra
      return thunkAPI.rejectWithValue(error.message);
    }
  }
);

Bước 3: Chỉnh sửa todoScreen.js để hiển thị chức năng SỬA

Trước lệnh return của component TodoScreen, bạn thêm hàm xử lý SỬA như sau:

  // Dành cho sửa: Cần có state lưu trạng thái đang sửa bản ghi nào
    const [editTitle, setEditTitle] = useState('');// chuỗi tiêu đề
    const [idEdit, setIdEdit] = useState(null); //lưu id bản ghi cần sửa
  
  const handleEdit = (id, title) =>{
        // hàm này để  ẩn hiện form sửa
        setEditTitle(title);
        setIdEdit(id);
    }
    // hàm lưu kết quả sửa
    const handleUpdate =()=>{ 

        let duLieuUpdate = {  title: editTitle};
        // dispatch( addTodo ( duLieuThem )  );

        dispatch(updateTodoApi({id: idEdit, data:duLieuUpdate}))
        .then((result) => {
            // console.log(result);

            console.log('Todo update successfully!');
            setEditTitle('');
            setIdEdit(null);
        })
        .catch((error) => {
            console.error('Error update todo:', error);
        });
    }

Không quên import updateTodoApi ở Screen nhé.

 

import { fetchTodos, deleteTodoApi, addTodoAPI,updateTodoApi} from '../redux/actions/todoAction'; 

Xuống dưới phần return cải tiến code toàn bộ phần hàm map để chỉnh trình bày giao diện sửa

 

{
                listTodo.map(row =>(
                    <View key={row.id} 
                     style={{margin:10,padding:10, borderColor:'blue', borderWidth:1}}>
                      
                      {
                          (idEdit === row.id)?
                              (<>
                                  <TextInput
                                          value={editTitle}
                                          onChangeText={setEditTitle}
                                          onBlur={handleUpdate}
                                          autoFocus
                                      />
                                      <Button title="Update" onPress={handleUpdate} />
                              </>
                              )
                          :
                              (
                                  <>
                                    <Text>{row.title}  -  {row.id}</Text>
                                      <TouchableOpacity onPress={()=>handleDeleteTodo(row.id)} >
                                          <Text style={{color: 'red'}}>Xóa</Text>
                                      </TouchableOpacity> 
  
  
                                      <TouchableOpacity onPress={() => handleEdit(row.id, row.title)}>
                                          <Text>Edit</Text>
                                      </TouchableOpacity>
                                  </>
  
                              )
                      }
                      
                    </View>  
                  ))
           }

Chạy lại ứng dụng để thử nghiệm thao tác sửa.

]]>
Tạo ứng dụng Todo với Redux có tương tác API trong Reactnative – P3 https://zezo.dev/view/tao-ung-dung-todo-voi-redux-co-tuong-tac-api-trong-reactnative-p3 Fri, 29 Nov 2024 17:05:55 +0000 https://zezo.dev/?p=242 Tiếp theo bài này, ít nhất bạn cần thực hiện thành công ví dụ mẫu ở phần 1: https://zezo.dev/view/tao-ung-dung-todo-voi-redux-co-tuong-tac-api-trong-reactnative-p1

Trong phần này sẽ thực hiện xây dựng chức năng Thêm mới

Bước 1: Chỉnh sửa Reducer thêm thao tác xử lý THÊM

Bên trong hàm builder của extraReducers, bạn thêm đoạn code xử lý kết Thêm addTodoAPI.fulfilled

builder.addCase(addTodoAPI.fulfilled, (state, action)=>{
              state.listTodo.push(action.payload);
          })
 		.addCase(addTodoAPI.rejected, (state, action) => {
            // Xử lý khi yêu cầu thêm todo bị từ chối hoặc xảy ra lỗi
            console.log('Add todo rejected:', action.error.message);
});

redux api p3

 

Bước 2: Tạo hàm addTodoAPI cho todoAction.js

Bạn vào file todoAction.js thêm hàm addTodoAPI như dưới đây. Chuỗi ‘todo/addTodoAPI’ là định danh xác định tên action.

export const addTodoAPI = createAsyncThunk(
    'todo/addTodoAPI',
    async (objTodo, thunkAPI) => {
      console.log(objTodo);
      try {
        // Gửi yêu cầu DELETE đến API để xóa todo
        const response = await fetch(api_url, {
          method: 'POST',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(objTodo)
        });
        const data = await response.json();
        // console.log(response);
        // Kiểm tra nếu status code là 200 hoặc 204 thì xóa thành công
        if (response.ok) {
            // console.log(response);
          // Sau khi xóa thành công, trả về id của todo đã xóa để cập nhật store
           return data; 
        } else {
          // Nếu có lỗi từ phía server, trả về lỗi
          const errorData = await response.json();
          return thunkAPI.rejectWithValue(errorData);
        }
      } catch (error) {
        // Xử lý lỗi nếu có bất kỳ lỗi nào xảy ra
        return thunkAPI.rejectWithValue(error.message);
      }
    }
  );

Bước 3: Chỉnh sửa todoScreen.js để hiển thị chức năng THÊM

Trước lệnh return của component TodoScreen, bạn thêm hàm xử lý THÊM như sau:

//Khai báo  state để thực hiện thêm
    const [title, setTitle] = useState('');
    
// hàm xử lý việc thêm
    const handleAddTodo = ()=>{
        let duLieuThem = {  title: title , status: false};
        // dispatch( addTodo ( duLieuThem )  );
        dispatch(addTodoAPI(duLieuThem))
        .then((result) => {
            // console.log(result);
            console.log('Todo add successfully!');
        })
        .catch((error) => {
            console.error('Error add todo:', error);
        });
    }

Trong phần return view bạn thêm ô nhập text và nút bấm thêm

 

<TextInput placeholder="Nhập công việc" onChangeText={setTitle} />
<View style={{width:100}}>
    <Button title="Thêm việc" onPress={handleAddTodo} />
</View>

Không quên import thêm component nhé

import {  Text, View ,TouchableOpacity,TextInput,Button} from "react-native";

import { fetchTodos, deleteTodoApi, addTodoAPI} from '../redux/actions/todoAction';

redux api p3

Bạn chạy lại ứng dụng để thử nghiệm kết quả

]]>
Tạo ứng dụng Todo với Redux có tương tác API trong Reactnative – P2 https://zezo.dev/view/tao-ung-dung-todo-voi-redux-co-tuong-tac-api-trong-reactnative-p2 Fri, 29 Nov 2024 17:03:35 +0000 https://zezo.dev/?p=238 Tiếp theo bài này, bạn cần thực hiện thành công ví dụ mẫu ở phần 1: https://zezo.dev/view/tao-ung-dung-todo-voi-redux-co-tuong-tac-api-trong-reactnative-p1

Ở phần 1 đã hiển thị được danh sách rồi, sang phần này sẽ làm chức năng xóa.

Bước 1: Chỉnh sửa Reducer thêm thao tác xử lý xóa

Bên trong hàm builder của extraReducers, bạn thêm đoạn code xử lý kết quả xóa

		// sau khi gọi api ở action xong trong này mới hoạt động
        builder.addCase(deleteTodoApi.fulfilled, (state, action) => {
            // Xóa todo
             state.listTodo = state.listTodo.filter(row => row.id !== action.payload);
            
        }) .addCase(deleteTodoApi.rejected, (state, action) => {
            // Xử lý khi yêu cầu xóa todo bị từ chối hoặc xảy ra lỗi
            console.log('Delete todo rejected:', action.error.message);
        });

extra reducer

 

Code trên có phần deleteTodoApi.fulfilled: Hàm deleteTodoApi ở trong file todoAction sẽ thực hiện gọi API, sau khi gọi API sẽ trả về một Promise (xem thêm tại đây https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). Promise sẽ trả về trạng thái fulfilled hoặc rejected. Nếu fulfilled thì là việc tương tác API thành công, nếu rejected thì là lỗi quá trình tương tác API hoặc server từ chối thao tác xóa.

Bước 2: Tạo hàm deleteTodoApi cho todoAction.js

Bạn vào file todoAction.js thêm hàm deleteTodoApi như dưới đây. Chuỗi ‘todo/deleteTodoApi’ là định danh xác định tên action.

export const deleteTodoApi = createAsyncThunk(
    'todo/deleteTodoApi',
    async (id, thunkAPI) => {
      try {
        // Gửi yêu cầu DELETE đến API để xóa todo
        const response = await fetch(`${api_url}/${id}`, {
          method: 'DELETE',
        });
        // console.log(response);
        if (response.ok) {
            // console.log(response);
          // Sau khi xóa thành công, trả về id của todo đã xóa để cập nhật store
          // action.payload ở trong reducer sẽ chính là id
           return id; 
        } else {
          // Nếu có lỗi từ phía server, trả về lỗi cho reducer
          const errorData = await response.json();
          return thunkAPI.rejectWithValue(errorData);
        }
      } catch (error) {
        // Xử lý lỗi nếu có bất kỳ lỗi nào xảy ra 
        return thunkAPI.rejectWithValue(error.message);
      }
    }
  );

 Bước 3: Chỉnh sửa todoScreen.js để hiển thị chức năng xóa

Trước lệnh return của component TodoScreen, bạn thêm hàm xử lý xóa như sau:

    // hàm xử lý xóa
    const handleDeleteTodo =async (id)=>{
        dispatch(deleteTodoApi(id))
            .then((result) => {
                console.log('Todo deleted successfully!');
            })
            .catch((error) => {
                console.error('Error deleting todo:', error);
            });
    }

Trong phần return view ở vòng lặp in danh sách các phần tử, bạn thêm nút bấm xóa cho dòng dữ liệu

 

<TouchableOpacity onPress={()=>handleDeleteTodo(row.id)} >
      <Text style={{color: 'red'}}>Xóa</Text>
</TouchableOpacity>

Chú ý thêm cả phần import cho TouchableOpacity để tránh lỗi

import {  Text, View ,TouchableOpacity} from "react-native";
import { fetchTodos, deleteTodoApi} from '../redux/actions/todoAction';

list todo

 

 

]]>