mobx系列(三)-在React中使用Mobx

Mobx提供了一个mobx-react包帮助开发者方便地在React中使用Mobx,mobx-react中有observer、Provider、inject几个常用的api。在《mobx系列(二)-mobx主要概念》中我们已经介绍过observer,本文介绍下inject、Provider,以及Mobx如何与React结合使用。

1、Provider

Provider是一个React组件,使用React的上下文(context)机制,可以用来向下传递stores,即把state传递给其子组件。

例如,有如下形式的一个store:

import {observable, computed, action} from 'mobx';
class userStoreClass {
    @observable user = {
      name: 'admin',
      role: '管理员'
    };
    count = 0;
    @computed get userName(){
        return this.user.name;
    }
    @action changeUser(){
      if(this.count % 2 === 1){
          this.user = {
              name: 'admin',
              role: '管理员'
          };
      }else{
          this.user.name = 'guest';
          this.user.role = '访客';
          this.user.isGuest = 'true';
      }
      this.count ++;
    }
}
const userStore = new userStoreClass();
export default userStore;

使用Provider传递store的方式为:

import React from 'react';
import ReactDOM from 'react-dom';
import {configure} from 'mobx';
import {Provider} from 'mobx-react';
import userStore from './models/userStore';
import App from './components/App';
// 状态始终需要通过动作来更新(实际上还包括创建)
configure({'enforceActions': 'always'});
ReactDOM.render((
    <Provider store={userStore}}>
        <App />
    </Provider>
  ), document.getElementById('container'));

如果有多个store,可以使用类似于如下形式:

const stores = {
  mainStore, userStore, commonStore
};
ReactDOM.render((
    <Provider {...stores}>
        <App />
    </Provider>
  ), document.getElementById('container'));

2、@inject

inject是一个高阶组件,作用是将组件连接到提供的stores。具体说是与Provider结合使用,从Provider提供给应用的state数据中选取所需数据,以props的形式传递给目标组件。用法为:

inject(stores)(component)
@inject(stores) class Component...

对应上节的例子,App内使用的组件User使用@inject方式为:

import React, {Component} from 'react';
import {inject, observer} from 'mobx-react';
import {Button} from 'antd';
import './style.css';
@inject( 'userStore')
@observer
export default class User extends Component{
    constructor(props){
        super(props);
        this.state = {};
    }
    render(){
	// 可以以this.props.userStore形式获取store内state
        const {user} = this.props.userStore;
	// 以.形式使用对象的属性值
        return(
            <div className='user'>
                <div className='user_list'>name:{user.name}</div>
                <div className='user_list'>role:{user.name}</div>
                <div className='user_list'>{user.isGuest ? `isGuest:${user.isGuest}` : ''}</div>
                <Button type='primary' onClick={() => this.props.userStore.changeUser()}>Change User</Button>
            </div>
        );
    }
}

3、react-mobx实践示例

我写了一个react-mobx的简单demo,地址为:https://github.com/smk108/mobx_demo

demo的结构为:

依据组件的划分使用了3个store,需要说明的是我是以class的形式创建的store,store中export的是store class的instance,例如第一节中userStore的形式:

const userStore = new userStoreClass();
export default userStore;

在React中使用Mobx的方式有很多,Mobx不会强制要求以某种方式使用它,我在demo中使用的方式仅仅是其中一种。事实上,你可以以任何你喜欢并且能生效的方式使用它。在下一篇《Mobx定义数据存储》中会介绍、对比文档推荐使用的数据存储和我的demo中使用的数据结构间的不同。

4、可观察的局部组件状态

Mobx允许在响应式React组件内使用自由地使用状态,意味着我们可以将一些状态像普通React组件一样管理,例如对上面提到的demo中User组件做如下修改:

import React, {Component} from 'react';
import {inject, observer} from 'mobx-react';
import {Button} from 'antd';
import Timer from '../Timer';
import './style.css';

@inject( 'userStore')
@observer
export default class User extends Component{
    constructor(props){
        super(props);
        this.state = {
            userChangeTimes: 0
        };
    }

    handleChangeUser(){
        this.props.userStore.changeUser();
        let {userChangeTimes} = this.state;
        userChangeTimes ++ ;
        this.setState({userChangeTimes});
    }

    render(){
        const {user} = this.props.userStore;
        return(
            <div className='user'>
                <div className='user_list'>name:{user.name}</div>
                <div className='user_list'>role:{user.name}</div>
                <div className='user_list'>{user.isGuest ? `isGuest:${user.isGuest}` : ''}</div>
                <div>user change times: {this.state.userChangeTimes}</div>
                <Button type='primary' onClick={this.handleChangeUser.bind(this)}>Change User</Button>
                <Timer />
            </div>
        );
    }
}

User组件state的userChangeTimes可以使用setState进行修改,一切都和不使用Mobx时一样。但是,推荐响应式组件应该没有或很少有状态,因为在与其他组件共享的对象中封装(视图)状态通常更方便。针对响应式组件需要维护单独状态的情况,Mobx为我们提供了更加方便的一种方式-可观察的局部组件状态。

Mobx允许使用@observable在React组件内引入可观察属性,意味着我们不需要通过React 的冗长和强制性的 setState 机制也可以在组件中拥有功能同样强大的本地状态(local state)。

用法如下(使用demo中Timer组件举例):

import React, {Component} from 'react';
import {inject, observer} from 'mobx-react';
import {observable, action} from "mobx";
import './style.css';

@inject('commonStore')
@observer
export default class Timer extends Component{
    constructor(props){
        super(props);
        this.state = {};
    }
    @observable secondsPassed = 0;

    componentWillMount(){
        this.props.commonStore.startTime();
        this.timer = setInterval(this.handleChangeSecondsPassed,1000);
    }

    @action.bound handleChangeSecondsPassed(){
        this.secondsPassed ++;
    }

    render(){
        const {time} = this.props.commonStore;
        return(
            <div className='time_content'>
                <div>{time}</div>
                <div>Seconds passed:{this.secondsPassed}</div>
            </div>
        );
    }
}

secondsPassed作为组件内可观察的局部状态,不使用setState也触发UI的响应。

需要注意的是:

  • 可观察局部状态会被render提取调用;
  • 可观察局部状态的修改会触发React的componentWillUpdate和componentDidUpdate生命周期,不会触发其它的生命周期;
  • 如果你需要使用React的其它生命周期方法,请使用基于state的常规React API;

5、生命周期钩子

当使用mobx-react时可以定义一个新的生命周期钩子函数componentWillReact,当组件因为它观察的状态发生改变时,组件会重新渲染,这时componentWillReact会触发,可以帮助追溯渲染并找到导致渲染的动作(action)。

修改demo中User组件举例如下:

import React, {Component} from 'react';
import {inject, observer} from 'mobx-react';
import {Button} from 'antd';
import Timer from '../Timer';
import './style.css';

@inject( 'userStore')
@observer
export default class User extends Component{
    constructor(props){
        super(props);
        this.state = {
            userChangeTimes: 0
        };
    }

    handleChangeUser(){
        this.props.userStore.changeUser();
        let {userChangeTimes} = this.state;
        userChangeTimes ++ ;
        this.setState({userChangeTimes});
    }

    componentWillReact() {
        console.log("I will re-render, since the user has changed!");
    }

    render(){
        const {user} = this.props.userStore;
        return(
            <div className='user'>
                <div className='user_list'>name:{user.name}</div>
                <div className='user_list'>role:{user.name}</div>
                <div className='user_list'>{user.isGuest ? `isGuest:${user.isGuest}` : ''}</div>
                <div>user change times: {this.state.userChangeTimes}</div>
                <Button type='primary' onClick={this.handleChangeUser.bind(this)}>Change User</Button>
                <Timer />
            </div>
        );
    }
}

需要注意的是:

  • componentWillReact 不接收参数;
  • componentWillReact 初始化渲染前不会触发 (使用 componentWillMount 替代);
  • componentWillReact 对于 mobx-react@4+, 当接收新的 props 时并在 setState 调用后会触发此钩子;
  • 像User组件内通过setState修改userChangeTimes也会触发此钩子;

6、React优化

本小节介绍几项基本的React优化策略,有些是基于在React中使用Mobx时特有的策略,有些是会用React通用的策略。

  • 使用大量的小组件

@observer 组件会追踪它们使用的所有值,并且当它们中的任何一个改变时重新渲染。 所以你的组件越小,它们需要重新渲染产生的变化则越小;这意味着用户界面的更多部分具备彼此独立渲染的可能性。

  • 在专用组件中渲染列表(避免多个组件受影响,一起重新渲染)
  • 不要使用数组的索引作为 key(虚拟dom)

不用使用数组索引或者任何将来可能会改变的值作为 key

  • 晚一点使用间接引用值

使用 mobx-react 时,推荐尽可能晚的使用间接引用值。 这是因为当使用 observable 间接引用值时 MobX 会自动重新渲染组件。 如果间接引用值发生在组件树的层级越深,那么需要重新渲染的组件就越少。

<think>嗯,用户想找HAL_TIM_PeriodElapsedCallback函数的定义位置和用法。首先,我需要回忆一下STM32的HAL库结构。这个回调函数应该和定时器中断相关,对吧?当定时器的周期到了,就会触发这个回调函数。 根据用户提供的引用资料,特别是引用[3],里面提到使用HAL_TIM_Base_Start_IT来启动定时器并开启中断,然后实现中断服务程序中的任务。所以HAL_TIM_PeriodElapsedCallback应该是在用户代码中定义的,用来处理周期性的中断事件。HAL库通常采用回调机制,用户需要自己实现这些回调函数,比如这个函数可能是在定时器更新事件时被调用。 接下来,关于定义的位置,HAL库中的弱定义应该在某个头文件或源文件中,比如stm32xx_hal_tim.h或者对应的.c文件中。用户需要在自己的代码中重新定义这个函数,覆盖弱符号。例如,在main.c或者专门的定时器处理文件中定义。 用法方面,用户需要在初始化定时器并启动中断后,编写这个回调函数的内容,比如翻转LED或者处理数据。同时要注意定时器实例的判断,如果有多个定时器的话,需要用htim的参数来选择正确的定时器。 可能用户遇到的问题是没有正确实现回调函数,或者没有启动中断。需要检查是否调用了正确的启动函数,比如HAL_TIM_Base_Start_IT而不是HAL_TIM_Base_Start,后者可能不会开启中断。 另外,用户可能需要知道如何查找HAL库中的弱定义,这通常是在HAL的源文件中,使用__weak关键字修饰。例如在stm32xx_hal_tim.c中会有这个函数的弱定义,用户在自己的代码中重新实现即可。 还需要提醒用户,中断服务函数的名字可能和CubeMX生成的有关,确保中断向量表正确,以及中断优先级设置合理。如果有多个定时器,回调函数中需要通过htim->Instance来判断是哪个定时器触发的,从而执行不同的操作。 总结一下,回答应该包括回调函数的定义位置在用户代码中,需要覆盖HAL的弱定义,用法是在定时器中断发生时执行用户代码,并注意定时器实例的判断。同时提供示例代码和常见问题解答,帮助用户正确使用。</think>### HAL_TIM_PeriodElapsedCallback 函数解析 #### 1. 函数定义位置 `HAL_TIM_PeriodElapsedCallback` 是STM32 HAL库中定时器更新事件(周期中断)的**弱定义回调函数**,其默认实现位于HAL库的定时器驱动文件中(例如:`stm32xx_hal_tim.c`),但用户需要在工程中**重新实现该函数**以覆盖默认的弱定义[^3]。 具体来说: - **弱定义位置**:`stm32xx_hal_tim.c`(例如`stm32h7xx_hal_tim.c`) - **用户实现位置**:用户代码中(如`main.c`或自定义的定时器处理文件) #### 2. 函数作用 当定时器的计数器溢出(或达到预设周期值)时,触发更新事件中断,此时`HAL_TIM_PeriodElapsedCallback` 会被调用,用于执行用户自定义的周期性任务,例如:LED闪烁、传感器数据采集等[^3]。 #### 3. 函数原型 ```c __weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { // 默认空实现(需用户覆盖) } ``` #### 4. 使用步骤 1. **定时器初始化** 配置定时器参数(预分频系数、计数周期等),例如: ```c TIM_HandleTypeDef htim3; htim3.Instance = TIM3; htim3.Init.Prescaler = 8399; // 84MHz/(8400) = 10kHz htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = 9999; // 10kHz/10000 = 1Hz HAL_TIM_Base_Init(&htim3); ``` 2. **启动定时器中断** 使用 `HAL_TIM_Base_Start_IT` 启动定时器并开启中断[^3]: ```c HAL_TIM_Base_Start_IT(&htim3); ``` 3. **实现回调函数** 在用户代码中重新定义函数: ```c void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM3) // 判断触发源 { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // 示例:翻转LED } } ``` #### 5. 注意事项 -使用多个定时器,需在回调函数中通过 `htim->Instance` 判断具体触发源[^3]。 - 必须调用 `HAL_TIM_Base_Start_IT`(而非 `HAL_TIM_Base_Start`)以启用中断功能。 - 确保中断服务函数 `TIMx_IRQHandler` 已正确关联到定时器(通常由CubeMX自动生成)。 --- ###
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值