Navigation
阅读进度0%
No headings found.

善省 - 第十三期 (23年1月)

December 19, 2024 (1y ago)

RXJS
JavaScript
React Native
CodePush
MobX

新的一年开始了,记录下你遇到的问题和 灵感吧!

RXJS Subject 和 BehaviorSubject

今年遇到的第一个问题,也是特别容易中招的地方,我们来看下面的这个代码,你觉得它有没有问题

const $ChatSession = new Subject<{ sessionEnd: boolean }>();
 
 
useEffect( () => {
	const ws = new WebSokcet('xxx')
  ws.onMessage((e) => {
    switch (e.action) {
      case 1:
          $ChatSession.next({sessionEnd: ture})
        break;
 
        case 2....:
        .....
        break;
    
      default:
        break;
    }
  })
 
  return () => {
    ws.close()
  }
},[])
 
const whenXXXXendChat = () => {
  // xxx 特定逻辑
  $ChatSession.next({sessionEnd: ture})
	// xxx 特定逻辑
}
 
const closeHandle = () => {
     $ChatSession.subscribe((sessionInfo) => {
        if (sessionInfo.sessionEnd) {
            hideChatWindow();
        }
    });
}
 
const closeHandle2 = () => {
   whenXXXXendChat()
   $ChatSession.subscribe((sessionInfo) => {
        if (sessionInfo.sessionEnd) {
            hideChatWindow();
        }
    });
}
 
你会发现 触发 closeHadle2 的时候很正常,但是到了 closeHandler 的时候就诡异了,它永远都不会 进入这个 if,因为 $ChatSession  流中已经没数据了!

那么如何修复这个BUG?

const $ChatSession = new BehaviorSubject<{ sessionEnd: boolean }>({ sessionEnd: false});
 

只需要改成 BehaviorSubject 就好了,官方文档() 每次定于的时候,它都会先去取一个值出来!

但是... 这样的编程设计真的好吗?这样的rxjs使用真的没问题吗?

关于 name = initName ?? "Joney"

请问这个时候 init传递了一个空,它会是什么呢?

const name = initName ?? "Joney";

注意! 特别要小心这个陷阱 ,当你的initName = " 的时候,它会是一个 "" 只有undefined 的时候才会是 Joney 所以你需要特别注意,想清楚你想要的是什么?是默认值都是 Joney还是允许它出现 "" 的情况呢?

问题 CodePush ERROR_CONFLICT

  1. 为什么会有 ERROR_CONFLICT ?

    对于 发布包的命令没有什么好说的,主要需要注意的是 由于code push 内部是对不同的版本包做了一次  “包指纹运算”,用以判断资源是否重复,如果你什么都不改 依然连续的 发布,那么不管是Staging /Production , 否会存在冲突(上一个版本的资源已经纯在,不需要再发布);

如果你还想改,那么请修改你的代码,让他们产生一些 不一样,这样在 code push 的 “包指纹运算” 中就能生产 新的 resource 了 ,于是不会产生 包冲突

  1. 如果发布的命令错了怎么办?

如果你发布命令错了,那么一般 有两类修改方式;  发布一个新版本(建议使用-简单)/使用回滚 功能

如何快速集成

从SellerApp的目前的经验来看

我们是通过代码进行集成 ,不放到 原生端里面去,但是原生端里面也是要遵循它的规则 比如1.x.x 版本,和 占位置的 配置字段 得留着

import {Button} from '@ant-design/react-native';
import React from 'react';
import {AppState, Text, View} from 'react-native';
import codePush from 'react-native-code-push';
import {Modal} from '../components/view';
import {Modal as AntdModal} from '@ant-design/react-native';
 
const codePushStatusDidChange = async (syncStatus: any) => {
  switch (syncStatus) {
    case codePush.SyncStatus.CHECKING_FOR_UPDATE:
      // 0 - 正在查询CodePush服务器以进行更新。
      console.info('[CodePush] Checking for update.');
      break;
    case codePush.SyncStatus.AWAITING_USER_ACTION:
      // 1 - 有可用的更新,并且向最终用户显示了一个确认对话框。(仅在updateDialog使用时适用)
      console.info('[CodePush] Awaiting user action.');
      break;
    case codePush.SyncStatus.DOWNLOADING_PACKAGE:
      // 2 - 正在从CodePush服务器下载可用更新。
      console.info('[CodePush] Downloading package.');
      break;
    case codePush.SyncStatus.INSTALLING_UPDATE:
      // 3 - 已下载一个可用的更新,并将其安装。
      codePush.notifyAppReady();
      console.info('[CodePush] Installing update.');
      break;
    case codePush.SyncStatus.UP_TO_DATE:
      // 4 - 应用程序已配置的部署完全最新。
      console.info('[CodePush] App is up to date.');
      break;
    case codePush.SyncStatus.UPDATE_IGNORED:
      // 5 该应用程序具有可选更新,最终用户选择忽略该更新。(仅在updateDialog使用时适用)
      console.info('[CodePush] User cancelled the update.');
      break;
    case codePush.SyncStatus.UPDATE_INSTALLED:
      // 6 - 安装了一个可用的更新,它将根据 SyncOptions 中的 InstallMode指定在 syncStatusChangedCallback 函数返回后立即或在下次应用恢复/重新启动时立即运行。
      console.info('[CodePush] Installed update.');
      codePush.notifyAppReady();
      break;
    case codePush.SyncStatus.SYNC_IN_PROGRESS:
      // 7 - 正在执行的 sync 操作
      console.info('[CodePush] Sync already in progress.');
      break;
    case codePush.SyncStatus.UNKNOWN_ERROR:
      // -1 - 同步操作遇到未知错误。
      console.info('[CodePush] An unknown error occurred.');
      break;
  }
};
 
const codePushDownloadDidProgress = (progress: {receivedBytes: number; totalBytes: number}) => {
  const curPercent = ((progress.receivedBytes / progress.totalBytes) * 100).toFixed(0);
  console.log('[CodePushUtils] Downloading Progress', `${curPercent}%`);
};
 
const syncImmediate = async (deploymentKey: string) => {
  codePush.sync(
    {
      updateDialog: {
        // 是否显示更新描述
        appendReleaseDescription: true,
        // 更新描述的前缀。 默认为"Description"
        descriptionPrefix: '\n\n更新内容:\n',
        // 强制更新按钮文字,默认为continue
        mandatoryContinueButtonLabel: '立即更新',
        // 强制更新时的信息. 默认为"An update is available that must be installed."
        mandatoryUpdateMessage: '必须更新后才能使用',
        // 非强制更新时,按钮文字,默认为"ignore"
        optionalIgnoreButtonLabel: '稍后',
        // 非强制更新时,确认按钮文字. 默认为"Install"
        optionalInstallButtonLabel: '后台更新',
        // 非强制更新时,检查到更新的消息文本
        optionalUpdateMessage: '有新版本了,是否更新?',
        // Alert窗口的标题
        title: '温馨提示',
      },
      deploymentKey,
      installMode: codePush.InstallMode.IMMEDIATE,
    },
    codePushStatusDidChange,
    codePushDownloadDidProgress,
  );
};
const showModal = (updateDetail: any, deploymentKey: string) => {
  const {isMandatory, description} = updateDetail;
  Modal.show(
    <AntdModal
      popup={true}
      style={{borderRadius: 12, paddingVertical: 12, width: 331}}
      visible={true}
      maskClosable={false}
      transparent={true}
    >
      <View style={{display: 'flex', marginTop: 0}}>
        <Text>{description}</Text>
        <View style={{flexDirection: 'row', justifyContent: 'center', marginTop: 10}}>
          {!isMandatory && (
            <Button
              onPress={() => {
                Modal.hide();
              }}
            >
              下次提醒
            </Button>
          )}
          <Button
            onPress={() => {
              syncImmediate(deploymentKey);
              Modal.hide();
            }}
          >
            后台更新
          </Button>
        </View>
      </View>
    </AntdModal>,
  );
};
 
export const checkForUpdate = async (deploymentKey: string) => {
  const update = await codePush.checkForUpdate(deploymentKey);
  // if (update) {
  // showModal(update, deploymentKey);
  syncImmediate(deploymentKey);
  // }
};
 
export const getUpdateMetadata = async () => {
  return await codePush.getUpdateMetadata(codePush.UpdateState.RUNNING);
};
 
export const codePushSync = (deploymentKey: string) => {
  AppState.addEventListener('change', (newState) => {
    newState === 'active' && syncImmediate(deploymentKey);
  });
};
 

🐟mbox 结合

import AppConfig from '@/config';
 
import {Platform} from 'react-native';
 
import {checkForUpdate, getUpdateMetadata} from '@/common/utils/codePushUtils';
import {makeAutoObservable, observable} from 'mobx';
 
class HotUpdateStore {
  // APP配置文件
 
  config = AppConfig;
 
  // 热更新环境
 
  buildType = '';
 
  // 更新key
 
  deploymentKey = '';
 
  // 热更新内容
  hotFixData = {};
 
  constructor() {
    makeAutoObservable(this);
  }
 
  // 设置热更新
  setHotUpdateConfig = async (config: any, updateBuildType: string) => {
    try {
      this.buildType = updateBuildType;
      if (config && config.codePush) {
        this.config = config;
      }
 
      const deploymentKey = config.codePush[Platform.OS][updateBuildType];
 
      checkForUpdate(deploymentKey);
      const meta = await getUpdateMetadata();
      if (meta) {
        this.setHotUpdateData(meta);
      }
    } catch (error) {}
  };
  setHotUpdateData = (data: any) => {
    this.hotFixData = data;
  };
}
 
const hotUpdateStore = new HotUpdateStore();
export default hotUpdateStore;
 
 

用的时候直接用就好啦

  const {setHotUpdateConfig} = useLocalObservable(() => store.hotUpdateStore);
  useEffect(() => {
    setFinish(false);
    const configInit = async () => {
      try {
        let config = localConfig;
        const isDebug = await getStorageDebug();
 
        if (isDebug === 'true') {
          console.log('配置开始载入');
          const [updateConfig, err] = await fetchConfig(configUrl);
          if (err) {
            console.log(`加载配置文件失败configUrl:${configUrl}`, err);
          }
          const storageConfig = await fetchStorageConfig();
          config = {
            ...localConfig,
            ...updateConfig,
            ...storageConfig,
          };
        }
        setFinish(true);
        if (__DEV__) {
          // console.log('合并后的配置文件', config);
        }
        setConfig(config);
        console.log('222');
        setHotUpdateConfig(config, config?.codePush?.buildType);
 
        if (config?.codePush?.open && !__DEV__) {
          // 热更新配置
          console.log('222');
          setHotUpdateConfig(config, config?.codePush?.buildType);
        }
      } catch (error) {
        console.log('加载配置文件失败', error);
      }
    };
    configInit();
  }, []);
 
// 配置如下 
......
  codePush: {
    open: true,
    isRelease: true,
    buildType: 'prodction',
    // ios android (staging、production)需要取消
    ios: {
      staging: '',
      prodction: '',
    },
    android: {
      staging: '',
      prodction: '',
    },
  },