初识mobx,以及mobx-react使用

六月底因为前司经济性裁员,10天内疯狂面试拿到好几个offer(外包字节、外包学而思和一些不知名的小公司),果然是树挪死人挪活,最后选择了还不错的公司,新公司主要是做机器人软件和云端管理软件的,前景还算可以。随后会整理最近的面经(已经在整理中了,是React方向),敬请期待。

新公司主要的技术栈是React+Typescript+carbonDesign+Mobx+GraphGL+Jest+nuxtjs,相较于上司,有很多需要扩展的知识点。

今天主要介绍下Mobx。

欢迎关注我的公众号:萌萌哒草头将军

Mobx简介

Mobx是响应式状态管理库,无关任何前端框架。现在已经发布到Mobx6了。在Mobx5之前,响应式原理是基于Object.defineProperty的,可以向下兼容到ES5浏览器,而从Mobx5开始使用proxy特性支持响应式,最低支持ES6浏览器。

相较于Redux,Mobx只强调下面三个概念

  • State(状态)

  • Actions(动作)

  • Derivations(派生)

一句话概括:在任何事件中调用action,修改state,如果这个state是响应式的,那么会通知基于这个state派生的计算值,或者触发派生的副作用。

其中派生属性可以分为两种情况

  • 计算值(computeds):类似于React的useMemo方法和Vue的computer方法

  • 副作用(reactions):类似于React的useEffect方法和Vue的WatchEffect方法

两种写法

通过Mobx定义一个响应式的Store有很多种方法,在不同的方法中定义他们也有所不同。

1. class写法

类的写法虽然笨重,但是适合模块化开发,易于扩展和维护。

import { makeObservable, observable, action, runInAction } from "mobx";

class MyStore {
    let tableList = [];
    let condition = {
        current: 1,
        pageSize: 10,
        // ...other
    };
    
    constructer () {
        constructor(title) {
            makeObservable(this, {
                tableList: observable,
                condition: observable,
                getTableList: action,
                onConditionChangez: action
            })
        
            // 设置 reaction,当 condition 变化时自动请求 tableList 数据
            reaction(
                () => this.condition, // 监听的表达式
                (condition) => this.getTableList(condition) // 触发的副作用
            )
            // 初始化时默认请求列表数据
            this.getTableList(this.condition);
        }
        // 计算属性
        get getInBeijing() {
            return this.tableList.map(item => item.address === "beijing");
        }
        getTableList (condition) {
            fetch("/v1/table/list", {
                method: "post",
                data: condition
            }).then((res) => {
                runInAction(() => {
                    this.tableList = res.data.list;
                })
            })
        }
        onConditionChange (condition) {
            this.condition = condition;
        }
    }
}
2.直接使用接口

使用接口创建对象的写法通常和组件结合在一起使用,虽然灵活,但是由于太过零散,不易于扩展。

import { observable, action } from "mobx"

funtion App () {
    const myStore = observable({
        tableList: [],
        condition: {
            pageSize: 10,
            current: 1,
            // ...other
        },
        // 计算属性
        get getInBeijing() {
            return this.tableList.map(item => item.address === "beijing");
        }
    })
    // 副作用
    reaction(
        () => myStore.condition,
        () => getTableList()
    };

    const getTableList = action(() => {
        fetch("/v1/table/list", {
            method: "post",
            data: myStore.condition
        }).then((res) => {
            runInAction(() => {
                myStore.tableList = res.data.list;
            })
        })
    })

    const onConditionChange = action(myStore) => {
        myStore.condition = condition
    }
    
    useEffect(() => {
        myStore.getTableList(myStore.condition);
    }, [])
    
    return <>
        <Condition condition={myState.condtion} onChange={onConditionChange} />
        <Table data={myState.tableList} onPaigionChange={onConditionChange} />
    </>
 }

副作用

关于副作用,除了上面使用的reaction方法还有autorunwhen。具体的使用如下:

  • autorun:当依赖属性值的变化执行一个指定的方法,自动收集依赖

  • reaction:将第一个方法的返回值作为第二个方法的参数,然后执行第二个方法

  • when:当第一个方法的返回值为true时,执行第二个方法

reaction(
    () => this.condition, // 监听的表达式
    (condition) => this.getTableList(condition) // 触发的副作用
)

when(
    () => !this.condition,
    () => console.log("数据为空")
)
// 当when方法没有设置第二个参数时,会返回Promise对象
when(() => !this.condition).then(() => {
   console.log("数据为空");
})

autorun(() => this.getTableList(this.condition))

另外记得,这些副作用都会返回销毁监听的方法,例如,在React使用Mobx时,通常我们会在组件销毁时清除他们

useEffect(() => {
    const dispose = reaction(
        () => this.condition, // 监听的表达式
        (condition) => this.getTableList(condition) // 触发的副作用
    )
    // return () => disopose()
    return dispose
}, [])

但是在实际的开发中,我们会使用具体的和框架相关的Mobx,mobx-react、mobx-vue。这样的好处是和组件结合的更紧密,而且不用每次都指定销毁方法。

Mobx-react的一些用法

在React中使用Mobx,通常有两个包:mobx-react、mobx-react-lite

  • mobx-react:提供类组件和hook组件的一些方法

  • mobx-react-lite:仅仅提供hook租价你的一些方法

在react的写法也有很多种,下面是几种常见的写法(我们依然使用上面的MyStore类)

1.依赖注入的写法

const myStore = new Mystore()
const StoreContext = createContext(myStore);

const App = observer(() => {
    const myState = useContext(StoreContext)
    
    return <>
        <Condition condition={myState.condtion} onChange={myState.onConditionChange} />
        <Table data={myState.tableList} onPaigionChange={myState.onConditionChange} />
    </>
})

ReactDOM.render(
    <StoreContext.Provider value={myStore}>
        <App />
    </StoreContext.Provider>,
    document.body
)

2.自定义hook钩子

const App = observer(() => {
    const [state] = useState(() => new Mystore())
    
    return <>
        <Condition condition={myState.condtion} onChange={state.onConditionChange} />
        <Table data={myState.tableList} onPaigionChange={state.onConditionChange} />
    </>
})

ReactDOM.render(<App />, document.body)

3.使用useLocalObservable钩子

useLocalObservable钩子可以看作是官方的自定义hook,上面的方法等价于下面的方法

const App = observer(() => {
    const state = useLocalObservable(() => new Mystore())
    
    return <>
        <Condition condition={myState.condtion} onChange={state.onConditionChange} />
        <Table data={myState.tableList} onPaigionChange={state.onConditionChange} />
    </>
})

ReactDOM.render(<App />, document.body)

observer函数和Observer组件的使用

从上面的示例中可以看到,组件都会使用observer函数包裹了,这是当state发生变化时,mobx确保组件重新渲染了。它的原理是一个高阶组件,后续会详细说说这个高阶组件的原理。

有的时候你需要使用render api的方式渲染组件,而且需要渲染的子组件是可观察的,那么可以使用Observer组件.

const Address = observer(({address}) => <div>{address}</>)

const App = () => {
    const state = useLocalObservable(() => new Mystore())
    
    return <Observer>
        {
            () => state.getInBeijing.map(address => <Address address={address} />)
        }
    </Observer>
}

注意,如果上面的Address组件如果需要是可观察的,需要使用observer函数或者Observer组件包裹

const Address = ({address}) => <div>{address}</>

const App = () => {
    const state = useLocalObservable(() => new Mystore())
    
    return <>
        {
            () => state.getInBeijing.map(address => <Observer>
                <Address address={address} />
            </Observer>)
        }
    </>
}

注解的写法

在mobx6开始已经不建议使用注解的写法了,但是你仍然可以使用这个功能。

使用时,需要先给idea设置注解识别功能,然后配置相关的babel插件,这里不展开了,详细的内容可以看看这里

今天的学习内容就这些了,下篇文章会先将整理好的面经发布出来,然后继续分享在新公司的一些学习笔记。希望可以帮助更多的人。文章如果有错误的地方欢迎指正!

因为新公司离家更近了,每天节省了两个小时的通勤时间,我会都用于更新学习笔记,所以一定要记得关注我的公众号:萌萌哒草头将军

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值