套接字回调内超出最大更新深度

问题描述 投票:0回答:0

我使用以下实现收到错误“超出最大更新深度”:

export type InstrumentDetailBaseState =
  | BaseState
  | {
      type: 'success';
      detail: InstrumentDetail;
    };

export interface RealTimeInstruments {
  [key: string]: InstrumentDetailBaseState;
}

export interface MarketsState {
  details: RealTimeInstruments;
}

export const initialState: MarketsState = {
  details: {},
};

const slice = createSlice({
  name: '/markets',
  initialState,
  reducers: {
    updateInstrumentDetail: (
      state,
      { payload }: PayloadAction<UpdateInstrumentDetailRequest>,
    ) => {
      const current = state.details[payload.key];
      const detail = current.detail;
      state.details[payload.key] = {
          type: 'success',
          detail: Lodash.merge({}, detail, payload.detail),
      };
    },
  },
});

export const { updateInstrumentDetail } = slice.actions;

在我的操作文件中我有这个:

export const useUpdateInstrumentDetailAction = () => {
  const dispatch = useAppDispatch();
  return useCallback(
    (request: UpdateInstrumentDetailRequest) => {
      dispatch(updateInstrumentDetail(request));
    },
    [dispatch],
  );
};

这是我的选择器实现:

export type AppSelector<Response, Request = null> = Selector<
  MainAppState,
  Response,
  Request
>;

const instrumentDetails: AppSelector<RealTimeInstruments> = ({
  marketsReducer,
}) => marketsReducer.details;

const instrumentDetailsKeyArgs: AppSelector<
  string,
  RealTimeInstrumentSelectorRequest
> = (_, { key }) => key;

const createDeepEqualSelector = createSelectorCreator(defaultMemoize, isEqual);

const createDetailItemSelector = () =>
  createDeepEqualSelector(
    instrumentDetails,
    instrumentDetailsKeyArgs,
    (details, key) => {
      const current = details[key];
      if (current?.type === 'success') {
        return current.detail;
      }
      return undefined;
    },
  );

export function useRealTimeInstrumentDetail(
  key: string,
  defaultItem?: InstrumentDetail | undefined,
): InstrumentDetail | undefined {
  const detailItemSelector = useMemo(createDetailItemSelector, []);

  const selector = useCallback(
    (state: BeTradingAppState) => detailItemSelector(state, { key }),
    [key, detailItemSelector],
  );

  const item = useAppSelector(selector);

  return useMemo(
    () => Lodash.merge({}, defaultItem, item),
    [defaultItem, item],
  );
}

我使用 lightstreamer 套接字来收听股市更新。

const useLightstreamerClient = () => useContext(LightstreamerContext);

export const useLightStreamerSubscription = (
  request: SubscriptionRequest,
  listener: SubscriptionListener,
) => {
  const client = useLightstreamerClient();

  useEffect(() => {
    const subscription = new Subscription(
      request.mode,
      request.items,
      request.fields,
    );
    subscription.setRequestedSnapshot('yes');
    subscription.addListener(listener);
    client?.subscribe(subscription);
    return () => {
      client?.unsubscribe(subscription);
    };
  }, [client, request, listener]);
};

最后我使用这个自定义挂钩来生成订阅。

在这种情况下,当 lightstreamer 发送多个“刻度”或“更新”并且调用 onItemUpdate 函数时,系统会抛出下一个错误:

Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.

注意:当我说“多个”时,我的意思是每个订阅密钥每秒更新 1 到 100 次。

export const useRealTimeInstrumentSubscription = (
  subscriptionKeys: string[],
) => {
  const updateInstrument = useUpdateInstrumentDetailAction();

  const subscriptionRequest = useMemo<SubscriptionRequest>(() => {
    return {
      mode: 'MERGE',
      items: subscriptionKeys,
      fields: [
        'id',
        'price',
        'volume',
      ],
    };
  }, [subscriptionKeys]);

  const subscriptionListener = useMemo<SubscriptionListener>(
    () => ({
      onItemUpdate: item => {
        const id: string = item.getValue('id');

        const priceValue = item.getValue('price');
        const volumeValue = item.getValue('volume');;


        const detail: RealTimeInstrumentDetail = {
          price: parseNumber(priceValue),
          volume: parseNumber(volumeValue),
        };

        updateInstrument({
          key: id,
          detail,
        });
      },
    }),
    [updateInstrument],
  );

  useLightStreamerSubscription(subscriptionRequest, subscriptionListener);
};

我使用这样的订阅:

在这种情况下,仪器阵列是静态的并且不处于状态。

const InstrumentsDataList: React.FC<{
  readonly instruments: Instrument[];
}> = ({ instruments }) => {

  const subscriptionKeys = useMemo(
    () => instruments.map(({ instrument }) => instrument.subscriptionKey),
    [instruments]
  );

  useRealTimeInstrumentSubscription(subscriptionKeys);

  return (
    <FlatList
      style={styles.container}
      data={instruments}
      keyExtractor={({ instrument }) => instrument.uniqueKey}
      renderItem={({ item }) => (
        <InstrumentDataListItem
          item={item}
        />
      )}
    />
  );
};

这是列表项:

const InstrumentDataListItem: React.FC<InstrumentDataListItemProps> = ({
  item,
}) => {
  const {
    price,
    spread,
    posture,
    variation: { unit, percentage },
  } = useRealTimeInstrumentDetail(item.instrument.uniqueKey, item.detail);

  return <View>...</View>;
};

export default InstrumentDataListItem;

即使我删除 FlatList,错误仍然存在。

const InstrumentsDataList: React.FC<{
  readonly instruments: Instrument[];
}> = ({ instruments }) => {

  const subscriptionKeys = useMemo(
    () => instruments.map(({ instrument }) => instrument.subscriptionKey),
    [instruments]
  );

  useRealTimeInstrumentSubscription(subscriptionKeys);

  return (
    <View/>
  );
};

当调用“updateInstrument”操作时,调试器会跟踪“onItemUpdate”函数中“useRealTimeInstrumentSubscription”内的错误。

我在列表项内使用

useRealTimeInstrumentDetail
钩子以获得更好的性能。 选择器已被记忆,但错误仍然存在。

react-native react-hooks react-redux lodash lightstreamer
© www.soinside.com 2019 - 2024. All rights reserved.