React Native+react-navigation+redux开发实用教程

本文详细介绍如何在ReactNative中结合Redux和react-navigation实现状态管理和导航功能。通过具体步骤指导开发者安装配置必要的库,并展示了如何利用Redux进行状态管理,以及如何在Android设备上处理物理返回键等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

React Native+Redux开发实用教程

为了帮助大家快速上手在React Native与Redux开发,在这本文中将向大家介绍如何在React Native中使用Redux和react-navigation组合?,以及一些必备基础以及高级知识

本参考了《新版React Native+Redux打造高质量上线App》课程的部分讲解,更多关于React Native与Redux的实战技巧可在《新版React Native+Redux打造高质量上线App》中查看。

那么如何在React Native中使用Redux和react-navigation组合?呢?

在使用 React Navigation 的项目中,想要集成 redux 就必须要引入 react-navigation-redux-helpers 这个库。

第一步:安装react-navigation-redux-helpers

npm install --save react-navigation-redux-helpers

第二步:配置Navigation

import React from 'react';
import {createStackNavigator, createSwitchNavigator} from 'react-navigation';
import {connect} from 'react-redux';
import {createReactNavigationReduxMiddleware, reduxifyNavigator} from 'react-navigation-redux-helpers';

export const rootCom = 'Init';//设置根路由

export const RootNavigator = createSwitchNavigator({
   ...
});

/**
 * 1.初始化react-navigation与redux的中间件,
 * 该方法的一个很大的作用就是为reduxifyNavigator的key设置actionSubscribers(行为订阅者)
 * 设置订阅者@https://github.com/react-navigation/react-navigation-redux-helpers/blob/master/src/middleware.js#L29
 * 检测订阅者是否存在@https://github.com/react-navigation/react-navigation-redux-helpers/blob/master/src/middleware.js#L97
 * @type {Middleware}
 */
export const middleware = createReactNavigationReduxMiddleware(
    'root',
    state => state.nav
);
 
/**
 * 2.将根导航器组件传递给 reduxifyNavigator 函数,
 * 并返回一个将navigation state 和 dispatch 函数作为 props的新组件;
 * 注意:要在createReactNavigationReduxMiddleware之后执行
 */
const AppWithNavigationState = reduxifyNavigator(RootNavigator, 'root');

/**
 * State到Props的映射关系
 * @param state
 */
const mapStateToProps = state => ({
    state: state.nav,//v2
});
/**
 * 3.连接 React 组件与 Redux store
 */
export default connect(mapStateToProps)(AppWithNavigationState);

提示:createReactNavigationReduxMiddleware方法要放到reduxifyNavigator之前执行否则会报错,以上代码片段的完整部分可以在课程源码中查找。

第二步:配置Reducer

import {combineReducers} from 'redux'
import theme from './theme'
import {rootCom, RootNavigator} from '../navigator/AppNavigators';

//1.指定默认state
const navState = RootNavigator.router.getStateForAction(RootNavigator.router.getActionForPathAndParams(rootCom));

/**
 * 2.创建自己的 navigation reducer,
 */
const navReducer = (state = navState, action) => {
    const nextState = RootNavigator.router.getStateForAction(action, state);
    // 如果`nextState`为null或未定义,只需返回原始`state`
    return nextState || state;
};

/**
 * 3.合并reducer
 * @type {Reducer<any> | Reducer<any, AnyAction>}
 */
const index = combineReducers({
    nav: navReducer,
    theme: theme,
});

export default index;

以上代码片段的完整部分可以在课程源码中查找。

第三步:配置store

import {applyMiddleware, createStore} from 'redux'
import thunk from 'redux-thunk'
import reducers from '../reducer'
import {middleware} from '../navigator/AppNavigators'

const middlewares = [
    middleware,
];
/**
 * 创建store
 */
export default createStore(reducers, applyMiddleware(...middlewares));

以上代码片段的完整部分可以在课程源码中查找。

第四步:在组件中应用

import React, {Component} from 'react';
import {Provider} from 'react-redux';
import AppNavigator from './navigator/AppNavigators';
import store from './store'

type Props = {};
export default class App extends Component<Props> {
    render() {
        /**
         * 将store传递给App框架
         */
        return <Provider store={store}>
            <AppNavigator/>
        </Provider>
    }
}

以上代码片段的完整部分可以在课程源码中查找。

经过上述4步呢,我们已经完成了react-navigaton+redux的集成,那么如何使用它呢?

使用react-navigaton+redux

1.订阅state

import React from 'react';
import {connect} from 'react-redux';

class TabBarComponent extends React.Component {
    render() {
        return (
            <BottomTabBar
                {...this.props}
                activeTintColor={this.props.theme}
            />
        );
    }
}

const mapStateToProps = state => ({
    theme: state.theme.theme,
});

export default connect(mapStateToProps)(TabBarComponent);

以上代码片段的完整部分可以在课程源码中查找。

在上述代码中我们订阅了store中的theme state,然后该组件就可以通过this.props.theme获取到所订阅的theme state了。

2.触发action改变state

import React, {Component} from 'react';
import {connect} from 'react-redux'
import {onThemeChange} from '../action/theme'
import {StyleSheet, Text, View, Button} from 'react-native';

type Props = {};
class FavoritePage extends Component<Props> {
    render() {
        return (
            <View style={styles.container}>
                <Text style={styles.welcome}>FavoritePage</Text>
                <Button
                    title="改变主题色"
                    onPress={() => {
                        // let {dispatch} = this.props.navigation;
                        // dispatch(onThemeChange('red'))
                        this.props.onThemeChange('#096');
                    }} 
                />
            </View>
        );
    }
}

const mapStateToProps = state => ({});

const mapDispatchToProps = dispatch => ({
    onThemeChange: (theme) => dispatch(onThemeChange(theme)),
});
export default connect(mapStateToProps, mapDispatchToProps)(FavoritePage);
...

以上代码片段的完整部分可以在课程源码中查找。

触发action有两种方式:

  • 一种是通过mapDispatchToProps将dispatch创建函数和props绑定,这样就可以通过this.props.onThemeChange('#096')调用这个dispatch创建函数来触发onThemeChange action了;

  • 另外一种方式是通过this.props中的navigation来获取dispatch,然后通过这个dispatch手动触发一个action:

    let {dispatch} = this.props.navigation;
    dispatch(onThemeChange('red'))
    

    以上代码片段的完整部分可以在课程源码中查找。

在Redux+react-navigation场景中处理 Android 中的物理返回键

在Redux+react-navigation场景中处理Android的物理返回键需要注意当前路由的所以位置,然后根据指定路由的索引位置来进行操作,这里需要用到BackHandler

import React, {Component} from 'react';
import {BackHandler} from "react-native";
import {NavigationActions} from "react-navigation";
import {connect} from 'react-redux';
import DynamicTabNavigator from '../navigator/DynamicTabNavigator';
import NavigatorUtil from '../navigator/NavigatorUtil';

type Props = {};

class HomePage extends Component<Props> {
    componentDidMount() {
        BackHandler.addEventListener("hardwareBackPress", this.onBackPress);
    }

    componentWillUnmount() {
        BackHandler.removeEventListener("hardwareBackPress", this.onBackPress);
    }

    /**
     * 处理 Android 中的物理返回键
     * https://reactnavigation.org/docs/en/redux-integration.html#handling-the-hardware-back-button-in-android
     * @returns {boolean}
     */
    onBackPress = () => {
        const {dispatch, nav} = this.props;
        //if (nav.index === 0) {
        if (nav.routes[1].index === 0) {//如果RootNavigator中的MainNavigator的index为0,则不处理返回事件
            return false;
        }
        dispatch(NavigationActions.back());
        return true;
    };

    render() {
        return <DynamicTabNavigator/>;
    }
}

const mapStateToProps = state => ({
    nav: state.nav,
});

export default connect(mapStateToProps)(HomePage);

以上代码片段的完整部分可以在课程源码中查找。

2end

API

combineReducers(reducers)

随着应用变得越来越复杂,可以考虑将 reducer 函数 拆分成多个单独的函数,拆分后的每个函数负责独立管理 state 的一部分。

函数原型:combineReducers(reducers)

  • 参数:reducers (Object): 一个对象,它的值(value)对应不同的 reducer 函数,这些 reducer 函数后面会被合并成一个。下面会介绍传入 reducer 函数需要满足的规则。

  • 每个传入 combineReducers 的 reducer 都需满足以下规则:

    • 所有未匹配到的 action,必须把它接收到的第一个参数也就是那个 state 原封不动返回。
    • 永远不能返回 undefined。当过早 return 时非常容易犯这个错误,为了避免错误扩散,遇到这种情况时 combineReducers 会抛异常。
    • 如果传入的 state 就是 undefined,一定要返回对应 reducer 的初始 state。根据上一条规则,初始 state 禁止使用 undefined。使用 ES6 的默认参数值语法来设置初始 state 很容易,但你也可以手动检查第一个参数是否为 undefined。

返回值

(Function):一个调用 reducers 对象里所有 reducer 的 reducer,并且构造一个与 reducers 对象结构相同的 state 对象。

combineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore 方法。

合并后的 reducer 可以调用各个子 reducer,并把它们返回的结果合并成一个 state 对象。 由 combineReducers() 返回的 state 对象,会将传入的每个 reducer 返回的 state 按其传递给 combineReducers() 时对应的 key 进行命名。

提示:在 reducer 层级的任何一级都可以调用 combineReducers。并不是一定要在最外层。实际上,你可以把一些复杂的子 reducer 拆分成单独的孙子级 reducer,甚至更多层。

createStore

函数原型:createStore(reducer, [preloadedState], enhancer)

参数

  • reducer (Function)::项目的根reducer。
  • [preloadedState] (any):这个参数是可选的, 用于设置 state 初始状态。这对开发同构应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致, 那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。
  • enhancer (Function): Store enhancer 是一个组合 store creator 的高阶函数,返回一个新的强化过的 store creator。这与 middleware 相似,它也允许你通过复合函数改变 store 接口。

返回值

  • (Store): 保存了应用所有 state 的对象。改变 state 的惟一方法是 dispatch action。你也可以 subscribe 监听 state 的变化,然后更新 UI。

示例

import { createStore } from 'redux'

function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return state.concat([action.text])
    default:
      return state
  }
}

let store = createStore(todos, ['Use Redux'])

store.dispatch({
  type: 'ADD_TODO',
  text: 'Read the docs'
})

console.log(store.getState())
// [ 'Use Redux', 'Read the docs' ]

以上代码片段的完整部分可以在课程源码中查找。

注意事项

  • 应用中不要创建多个 store!相反,使用 combineReducers 来把多个 reducer 创建成一个根 reducer。
  • 你可以决定 state 的格式。你可以使用普通对象或者 Immutable 这类的实现。如果你不知道如何做,刚开始可以使用普通对象。
  • 如果 state 是普通对象,永远不要修改它!比如,reducer 里不要使用 Object.assign(state, newData),应该使用 Object.assign({}, state, newData)。这样才不会覆盖旧的 state。如果可以的话,也可以使用 对象拓展操作符(object spread spread operator 特性中的 return { …state, …newData }。
  • 对于服务端运行的同构应用,为每一个请求创建一个 store 实例,以此让 store 相隔离。dispatch 一系列请求数据的 action 到 store 实例上,等待请求完成后再在服务端渲染应用。
  • 当 store 创建后,Redux 会 dispatch 一个 action 到 reducer 上,来用初始的 state 来填充 store。你不需要处理这个 action。但要记住,如果第一个参数也就是传入的 state 是 undefined 的话,reducer 应该返回初始的 state 值。
  • 要使用多个 store 增强器的时候,你可能需要使用 compose

applyMiddleware

函数原型:applyMiddleware(...middleware)

使用包含自定义功能的 middleware 来扩展 Redux。

技巧

  • react-navigation+redux;
  • 如何防止重复创建实例:
    • 方式一:单例+Map+工厂;
    • 方式二:页面保存实例变量,传递给,Action使用;
    • 方式三:在action中创建实例;
  • 如何动态的设置store,和动态获取store(难点:storekey不固定);
  • 如何实现可取消的redux action:可参考SearchPage的设计;

上述的实战技巧可在新版React Native+Redux打造高质量上线App中获取;

问答

  • Redux是如何实现JS的可预测状态的管理?
    • 单一数据源;
    • 所有数据都是只读的,要想修改数据,必须 dispatch 一个 action 来描述什么发生了改变;
    • 当处理 action 时,必须生成一个新的 state,不得直接修改原始对象;
  • Redux的核心思想是什么?
    • 单向数据流的是Redux架构的设计核心;

如何做到从不直接修改 state ?

从不直接修改 state 是 Redux 的核心理念之一:为实现这一理念,可以通过一下两种方式:

[1.通过Object.assign()创建对象拷贝, 而拷贝中会包含新创建或更新过的属性值](https://coding.imooc.com/class/304.html)

在下面的 todoApp 示例中, Object.assign() 将会返回一个新的 state 对象, 而其中的 visibilityFilter 属性被更新了:

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    default:
      return state
  }
}

以上代码片段的完整部分可以在课程源码中查找。

[2. 通过通过ES7的新特性对象展开运算符(Object Spread Operator)](https://coding.imooc.com/class/304.html)
function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return { ...state, visibilityFilter: action.filter }
    default:
      return state
  }
}

以上代码片段的完整部分可以在课程源码中查找。

这样你就能轻松的跳回到这个对象之前的某个状态(想象一个撤销功能)。

总结

  • Redux 应用只有一个单一的 store。当需要拆分数据处理逻辑时,你应该使用 reducer 组合 而不是创建多个 store;
  • redux一个特点是:状态共享,所有的状态都放在一个store中,任何component都可以订阅store中的数据;
  • 并不是所有的state都适合放在store中,这样会让store变得非常庞大,如某个状态只被一个组件使用,不存在状态共享,可以不放在store中;

未完待续

参考

D:\AwesomeProject>npm ls react AwesomeProject@0.0.1 D:\AwesomeProject +-- @ant-design/icons-react-native@2.3.2 | `-- react@17.0.2 deduped +-- @ant-design/react-native@5.4.2 | +-- @floating-ui/react-native@0.10.7 | | `-- react@17.0.2 deduped | +-- rc-field-form@2.7.0 | | `-- react@17.0.2 deduped | +-- rc-util@5.44.4 | | `-- react@17.0.2 deduped | +-- react-native-collapsible@1.6.2 | | `-- react@17.0.2 deduped | +-- react-native-modal-popover@2.1.3 | | `-- react@17.0.2 deduped | `-- react@17.0.2 deduped +-- @react-native-community/cameraroll@4.1.2 | `-- react@17.0.2 deduped +-- @react-native-community/masked-view@0.1.11 | `-- react@17.0.2 deduped +-- @react-native-community/segmented-control@2.2.2 | `-- react@17.0.2 deduped +-- @react-native-community/slider@3.0.3 | `-- react@17.0.2 deduped +-- @react-native-community/viewpager@4.2.4 | `-- react@17.0.2 deduped +-- @react-native-picker/picker@1.16.8 | `-- react@17.0.2 deduped +-- @react-navigation/native-stack@6.11.0 | +-- @react-navigation/elements@1.3.31 | | `-- react@17.0.2 deduped | `-- react@17.0.2 deduped +-- @react-navigation/native@6.1.18 | +-- @react-navigation/core@6.4.17 | | +-- react@17.0.2 deduped | | `-- use-latest-callback@0.2.4 | | `-- react@17.0.2 deduped invalid: "^16.0.0" from node_modules/react-native-elements/node_modules/react-native-ratings | `-- react@17.0.2 deduped +-- @rneui/base@4.0.0-rc.8 | `-- react-native-ratings@8.1.0 | `-- react@17.0.2 deduped +-- react-native-elements@1.2.7 | `-- react-native-ratings@6.5.0 | `-- react@17.0.2 deduped invalid: "^16.0.0" from node_modules/react-native-elements/node_modules/react-native-ratings +-- react-native-exception-handler@2.10.10 | `-- react@17.0.2 deduped +-- react-native-image-picker@3.8.1 | `-- react@17.0.2 deduped +-- react-native-image-zoom-viewer@3.0.1 | +-- react-native-image-pan-zoom@2.1.12 | | `-- react@17.0.2 deduped invalid: "^16.0.0" from node_modules/react-native-elements/node_modules/react-native-ratings | `-- react@17.0.2 deduped +-- react-native-linear-gradient@2.8.3 | `-- react@17.0.2 deduped +-- react-native-permissions@3.8.0 | `-- react@17.0.2 deduped +-- react-native-reanimated@2.14.4 invalid: ">=3.10.1" from node_modules/@ant-design/react-native | `-- react@17.0.2 deduped +-- react-native-safe-area-context@3.3.2 | `-- react@17.0.2 deduped +-- react-native-screens@3.15.0 | +-- react-freeze@1.0.4 | | `-- react@17.0.2 deduped invalid: "^16.0.0" from node_modules/react-native-elements/node_modules/react-native-ratings | `-- react@17.0.2 deduped +-- react-native-svg-charts@5.4.0 | `-- react@17.0.2 deduped +-- react-native-webview@11.26.1 | `-- react@17.0.2 deduped +-- react-native@0.68.0 | +-- react-shallow-renderer@16.14.1 | | `-- react@17.0.2 deduped invalid: "^16.0.0" from node_modules/react-native-elements/node_modules/react-native-ratings | +-- react@17.0.2 deduped | `-- use-subscription@1.11.0 | +-- react@17.0.2 deduped invalid: "^16.0.0" from node_modules/react-native-elements/node_modules/react-native-ratings | `-- use-sync-external-store@1.5.0 | `-- react@17.0.2 deduped invalid: "^16.0.0" from node_modules/react-native-elements/node_modules/react-native-ratings +-- react-redux@7.2.9 | `-- react@17.0.2 deduped +-- react-test-renderer@17.0.2 | `-- react@17.0.2 deduped `-- react@17.0.2 npm error code ELSPROBLEMS npm error invalid: react@17.0.2 D:\AwesomeProject\node_modules\react npm error A complete log of this run can be found in: C:\Users\临时\AppData\Local\npm-cache\_logs\2025-08-01T01_11_42_653Z-debug-0.log
最新发布
08-02
<think>好的,我现在需要分析用户提供的`npm ls react`输出,找出可能导致React Hooks错误的原因。首先,用户之前遇到了“Invalid hook call”错误,这通常与React版本不一致或多个实例有关。 首先看用户的依赖树,最顶层的`react`版本是17.0.2,大部分依赖都标记了`deduped`(去重),说明它们共享了顶层的React实例。但注意到有几个依赖项标记了`invalid`,例如: - `node_modules/react-native-elements/node_modules/react-native-ratings`中要求`react@^16.0.0`,但实际使用的是17.0.2。 - `react-native-image-pan-zoom`、`react-freeze`、`react-shallow-renderer`、`use-subscription`、`use-sync-external-store`等依赖也有类似的`invalid`提示,都是因为它们的`peerDependencies`要求React版本为16.x,但项目中实际用了17.0.2。 另外,还有一个更严重的问题:`react-native-reanimated`标记为`invalid`,因为`@ant-design/react-native`要求它的版本`>=3.10.1`,但用户安装的是2.14.4,版本不匹配。 这些版本冲突可能导致React实例不一致或依赖解析错误,从而引发Hooks调用异常。具体来说: 1. **Peer Dependency 不兼容**:部分依赖(如`react-native-ratings`)的`peerDependencies`要求React 16.x,但项目使用React 17.x,虽然npm/yarn可能允许安装,但可能导致运行时问题,因为这些库可能未完全兼容React 17的Hooks机制。 2. **react-native-reanimated 版本不匹配**:`@ant-design/react-native`需要`react-native-reanimated >=3.10.1`,但用户安装了2.14.4,这可能导致该库无法正确初始化,进而影响React Native的AppRegistry注册,引发第二个错误(“Module AppRegistry is not registered”)。 3. **依赖解析混乱**:尽管大部分依赖共享了顶层的React 17.0.2,但部分子依赖的`invalid`标记可能导致npm在解析时生成不一致的模块树,可能间接导致React实例被多次加载(例如某些库可能尝试从自己的`node_modules`中引入React)。 解决步骤应针对这些具体的版本冲突: - **升级 react-native-reanimated 到符合要求的版本**(>=3.10.1),以满足`@ant-design/react-native`的依赖需求。 - **处理 react-native-ratings 等库的React版本不兼容**:检查`react-native-elements`和`react-native-ratings`的版本是否有支持React 17的更新版本,或者考虑替换为其他兼容的组件库。 - **清理并重新安装依赖**:删除`node_modules`和锁文件,重新安装以确保依赖解析正确。 - **检查 peerDependencies 警告**:安装时注意npm/yarn的警告信息,手动安装符合要求的peer依赖(如果必要)。 </think>根据你提供的`npm ls react`输出,问题主要集中在**依赖版本冲突**和**Peer Dependency 不兼容**,这是导致“Invalid hook call”和“AppRegistry未注册”错误的核心原因。以下是具体分析和解决方法: ### 一、关问题定位 从依赖树可以看到两个核心冲突: #### 1. `react-native-reanimated` 版本不匹配 `@ant-design/react-native` 要求 `react-native-reanimated >=3.10.1`,但你当前安装的是 `2.14.4`(标记为`invalid`)。 这会导致 `@ant-design/react-native` 无法正常初始化,可能引发React Native核心模块(如AppRegistry)加载失败,从而出现“Module AppRegistry is not registered”错误。 #### 2. 多个依赖的 Peer Dependency 与 React 17 不兼容 例如: - `react-native-ratings`(来自`react-native-elements`)要求 `react@^16.0.0`,但实际使用 `17.0.2`(标记为`invalid`); - `react-freeze`、`react-shallow-renderer`等依赖也存在类似的React 16.x 版本要求,但项目用了React 17.x。 虽然npm允许安装不兼容的Peer Dependency,但这些库可能未完全适配React 17的Hooks机制,导致Hooks调用上下文异常,引发“Invalid hook call”错误。 ### 二、解决步骤 #### 1. 修复 `react-native-reanimated` 版本冲突 `@ant-design/react-native` 需要 `react-native-reanimated >=3.10.1`,而React Native 0.68.x 兼容的 `react-native-reanimated` 最新版本是 **3.x**(需确认兼容性)。 **操作步骤**: ```bash # 卸载旧版本 npm uninstall react-native-reanimated # 安装兼容React Native 0.68.x的最新3.x版本(例如3.3.0,具体版本需查官方文档) npm install react-native-reanimated@3.3.0 ``` #### 2. 解决 `react-native-ratings` 等库的React版本不兼容 `react-native-ratings` 6.5.0(来自`react-native-elements@1.2.7`)仅支持React 16.x,而你使用React 17.x。 **解决方案**: - **升级 `react-native-elements` 到更高版本**:新版本可能已修复Peer Dependency问题。例如,`react-native-elements@4.x` 可能支持React 17/18。 执行:`npm install react-native-elements@latest`(注意检查是否兼容React Native 0.68.x)。 - **替换为其他组件库**:如果升级后仍有问题,考虑使用其他兼容React 17的评分组件(如`react-native-star-rating`)。 #### 3. 清理依赖并重新安装 版本冲突可能导致缓存残留,需彻底清理后重新安装: ```bash # 删除依赖和锁文件 rm -rf node_modules package-lock.json # 重新安装 npm install ``` #### 4. 验证依赖一致性 重新安装后,再次运行 `npm ls react`,确认所有`invalid`标记消失,且所有依赖指向同一React 17.0.2实例。 ### 三、额外注意事项 - **React Native 0.68.x 兼容版本**:确保所有依赖(如`@ant-design/react-native`)兼容React Native 0.68.x。例如,`@ant-design/react-native@5.4.2` 是否支持0.68.x需查看官方文档。 - **Hooks 调用检查**:即使依赖修复,仍需确认代码中Hooks是否在函数组件顶层调用(无循环/条件/类组件中调用)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值