前言

参考博主【不羁的木木】做的笔记,顺便把自己看不懂的地方梳理,写一篇博客。下面为博主链接:

【开源鸿蒙跨平台开发学习笔记】Day04:React Native 开发 HarmonyOS-GitCode口袋工具开发-2-CSDN博客https://blog.csdn.net/qiaomu8559968/article/details/155223711?spm=1001.2014.3001.5502

一、踩坑

1.1 未加载Star的仓库

如果调用了已经starred的仓库的列表的接口"",但是列表却没显示出来,可能有以下两个原因。

https://api.gitcode.com/api/v5/users/:username/starred

1.你从未Starred过,也就不会显示有Star显示出来。所以这个时候你需要去GitCode的官网Star几个项目。

2.运行项目出现模拟器里面报错的情况,这个是因为React的代码里面有错,修改正确重新编译,再粘贴编译产生出来的文件即可。

二、编写React端的鸿蒙代码

2.1 修改代码

1.修改src/api目录下的client.tsx

import axios from 'axios';

// 默认令牌(开发调试用)
const DEFAULT_TOKEN = '你的GitCode令牌';
let privateToken = DEFAULT_TOKEN;

// 设置令牌的函数
export function setPrivateToken(token?: string) {
  if (token) {
    privateToken = token;
  }
}

// 创建 axios 实例
export const http = axios.create({
  baseURL: 'https://api.gitcode.com/api/v5/',
  timeout: 10000,
});

// 请求拦截器 - 自动添加认证头
http.interceptors.request.use(config => {
  const headers = config.headers ?? {};
  headers['private-token'] = privateToken;  // 添加认证令牌
  config.headers = headers;
  return config;
});

// 响应拦截器
http.interceptors.response.use(
  res => res,  // 直接返回成功响应
  err => Promise.reject(err),  // 传递错误
);

// 错误信息格式化函数
export function getErrorMessage(error: unknown): string {
  if (axios.isAxiosError(error)) {
    const status = error.response?.status;
    const data = error.response?.data as any;
    const msg = typeof data === 'string'
      ? data
      : data?.message || data?.error || error.message;
    return status ? `${status} ${msg}` : msg;
  }
  const e = error as any;
  return String(e?.message || e);
}

2.修改src/api目录下的user.tsx

import {http} from './client';
import {UserProfile} from '../types/user';

// 支持查询指定用户或使用默认用户
export async function fetchUserProfile(username: string): Promise<UserProfile> {
  const res = await http.get<UserProfile>(`users/${encodeURIComponent(username)}`);
  return res.data;
}

export async function fetchStarred(username: string): Promise<any[]> {
  const res = await http.get<any[]>(`users/${username}/starred`);
  return res.data;
}

3.修改src/screens目录下的ExploreScreen.tsx(注意:这里的Deng666为我在GitCode的昵称,可以改成你自己的。)

Promise.all([fetchUserProfile('Deng666'), fetchStarred('Deng666')])

import React, {useEffect, useState} from 'react';
import {
  View,
  Text,
  StyleSheet,
  Image,
  ActivityIndicator,
  ScrollView,
  Pressable,
  Linking,
} from 'react-native';
import {fetchUserProfile, fetchStarred} from '../api';
import {UserProfile} from '../types/user';
import {getErrorMessage} from '../api/client';

// 已经是 ExploreScreen 和默认导出
export default function ExploreScreen(): JSX.Element {
  const [data, setData] = useState<UserProfile | null>(null);
  const [loading, setLoading] = useState<boolean>(false);
  const [error, setError] = useState<string>('');
  const [starred, setStarred] = useState<any[]>([]);

  useEffect(() => {
    let mounted = true;
    setLoading(true);
    setError('');
    Promise.all([fetchUserProfile('Deng666'), fetchStarred('Deng666')])
      .then(([d, s]) => {
        if (!mounted) {
          return;
        }
        setData(d);
        setStarred(Array.isArray(s) ? s : []);
      })
      .catch(e => {
        if (!mounted) {
          return;
        }
        setError(getErrorMessage(e));
      })
      .finally(() => {
        if (!mounted) {
          return;
        }
        setLoading(false);
      });
    return () => {
      mounted = false;
    };
  }, []);

  // 安全的打开链接函数
  const safeOpenURL = (url: string | undefined) => {
    if (url && typeof url === 'string' && url.trim() !== '') {
      Linking.openURL(url);
    } else {
      console.warn('无效的 URL:', url);
    }
  };

  if (loading) {
    return (
      <View style={styles.center}>
        <ActivityIndicator />
        <Text style={styles.loadingText}>加载中</Text>
      </View>
    );
  }

  if (error) {
    return (
      <View style={styles.center}>
        <Text style={styles.errorText}>请求失败:{error}</Text>
      </View>
    );
  }

  if (!data) {
    return (
      <View style={styles.center}>
        <Text style={styles.errorText}>暂无数据</Text>
      </View>
    );
  }

  return (
    <ScrollView contentContainerStyle={styles.scrollContent}>
      <Image source={{uri: data.avatar_url}} style={styles.avatar} />
      <Text style={styles.title}>{data.name || data.login}</Text>
      <Text style={styles.subtitle}>类型:{data.type}</Text>
      <Text style={styles.subtitle}>
        粉丝:{data.followers},关注:{data.following}
      </Text>
      {Boolean(data.bio) && <Text style={styles.bio}>{data.bio}</Text>}
      <Pressable
        onPress={() => safeOpenURL(data.html_url)}
        style={styles.linkButton}>
        <Text style={styles.linkText}>打开主页</Text>
      </Pressable>
      <View style={styles.listHeader}>
        <Text style={styles.listHeaderText}>已 Star 的仓库</Text>
      </View>
      {starred.map((item, idx) => {
        const name =
          item?.name || item?.path || item?.project_name || '未知仓库';
        const desc = item?.description || '';
        const link = item?.html_url || item?.web_url || item?.url || '';
        return (
          <View key={`${name}-${idx}`} style={styles.repoCard}>
            <Text style={styles.repoName}>{name}</Text>
            {!!desc && <Text style={styles.repoDesc}>{desc}</Text>}
            {!!link && (
              <Pressable
                onPress={() => safeOpenURL(link)}
                style={styles.repoLinkBtn}>
                <Text style={styles.repoLinkText}>访问仓库</Text>
              </Pressable>
            )}
          </View>
        );
      })}
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  center: {flex: 1, alignItems: 'center', justifyContent: 'center'},
  loadingText: {marginTop: 8, fontSize: 14, color: '#666'},
  errorText: {fontSize: 14, color: '#d00'},
  scrollContent: {alignItems: 'center', paddingVertical: 24},
  avatar: {width: 120, height: 120, borderRadius: 60, backgroundColor: '#eee'},
  title: {marginTop: 16, fontSize: 24, fontWeight: '700'},
  subtitle: {marginTop: 8, fontSize: 16, color: '#666'},
  bio: {
    marginTop: 12,
    fontSize: 14,
    color: '#333',
    paddingHorizontal: 24,
    textAlign: 'center',
  },
  linkButton: {
    marginTop: 16,
    paddingHorizontal: 16,
    paddingVertical: 10,
    borderRadius: 6,
    backgroundColor: '#007aff',
  },
  linkText: {color: '#fff', fontSize: 14, fontWeight: '600'},
  listHeader: {width: '100%', paddingHorizontal: 24, paddingTop: 24},
  listHeaderText: {fontSize: 18, fontWeight: '600'},
  repoCard: {
    width: '92%',
    marginTop: 12,
    padding: 12,
    borderRadius: 8,
    backgroundColor: '#f7f7f7',
  },
  repoName: {fontSize: 16, fontWeight: '600'},
  repoDesc: {marginTop: 6, fontSize: 14, color: '#555'},
  repoLinkBtn: {
    marginTop: 10,
    alignSelf: 'flex-start',
    paddingHorizontal: 12,
    paddingVertical: 8,
    borderRadius: 6,
    backgroundColor: '#34c759',
  },
  repoLinkText: {color: '#fff', fontSize: 14, fontWeight: '600'},
});

2.2 执行编译命令

执行编译的命令'npm run dev",将生成的"\AwesomeProject\harmony\entry\src\main\resources\rawfile"目录下的"bundle.harmony.js"拷贝到鸿蒙项目里的"rawfile"目录下。最后,编译运行项目,可以看到,项目已经显示出来我们Starred过的项目。(如果没显示,可能是因为你从未Starred过,可以去GitCode官网给几个项目点Star~)

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐