npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

meng

v3.4.6

Published

an high order component for lift state and inject data source

Downloads

226

Readme

mengbi

an arbitrary magic react state management for rxjs enthusiast.

安装

npm i meng --save

灵感

众所周知,react是一个侧重同步数据到dom的库,也就是(data) => dom. 这里的data包含了propsstate,但是其他的数据源不好管理,比如说api。 而现有的方案用起来都太复杂了,本来(data) => dom一个函数能解决的问题,用了flux架构比如redux之后,需要的模块*3。所以能不能用一种简单的方式把所有数据源都抽象成data, 并且能用react的方式解决问题呢,答案是肯定的,我们可以这么设计({props, state, api}) => dom。于是就有了meng--一个把所有数据源都抽象合并成一种数据类型的库。 由于只有rxjs能帮我们实现流的特性,所以[email protected]是强依赖rxjs的。

api

Store

每一个meng组件都有自己的store,store的结构是这样的:

interface Store<S> {
    state$: ReplaySubject<S> // store的触发器
    store$: Observable<S> // store的状态容器
    children: { [key: string]: Store<Object> } //存放子store,因为是扁平结构,所以只有根store的children才有子节点
    setState: Function, // 设置store$的状态
    subscribe: (success: (state: Object) => void, error?: (error: Error) => void, complete?: () => void) => Subscription // 订阅store$
}

所有的meng组件的状态又都会放在根节点的children里面,所以meng支持直接修改和订阅另一个组件的状态。也可以把store想象成ng的rootScope, 而children里面的store相当于ng里controller对应的scope。下面是一段demo:

import Store from 'meng'

Store.children.App.setState({user: {name: "corol"}}, error => {})

lift: (initialState, initialName) => React.Component

lift函数可以把react组件提升为meng组件,他只有两个参数:

  • initialState 初始化meng组件的状态
  • initialName 设置store的名字,如果不显式的设置,则默认使用displayName、函数名称、随机一个名字,按优先级排序。

inject: (Resource, string | (currentState, nextState) => object) => React.Component

  • Resource 给组件注入数据源,可以是promise,也可以是其他组件的store,也可以是函数。如果是函数,则第一个参数是 initialState,包括父级传下来的属性。
  • selector 注入到react组件的props的变量的名称,可以是stirng也可以是返回一个对象(会覆盖store里的其他状态)

listen: ((currentStore, nextStore) => any, string | (nextState, currentState) => object) => React.Component

可以监听 lift 里面的状态和其他 injected 数据源. 从3.4.0开始,也可以监听自己和其他 listen 数据源了,所以要判断好循环边界哦.

error: (errorHandler: (err: any) => void) => React.Component

在 meng 把状态提升之后,状态不在只是 Object 了,它现在是 Maybe(Observable),有可能会失败。输出成功的结果到组件,输出错误的结果到这个 errorhandler 里. 如果出错且没有注册 error,则什么事情都不会发生。如果已经注册,则调用它。注意,如果是通过 setState 传进去一个错误的 Observable 进去的话,只能在 setState 的 callback 捕获到异常信息

Example

展示一段我用meng写的博客里的代码吧:

const listenList = (currentStore: Props, nextStore: Props) => {
  return currentStore.latest !== nextStore.latest ? list("C0PKC07FB", nextStore.latest) : null
}

const listSelector = (currentState: Props, state: ISlackListType) => {
  const previousMessages = (currentState.post && currentState.post.messages) || []
  state.messages = previousMessages.concat(state.messages)
  return { post: state }
}

@inject(connect, "newmsg")
@listen(listenList, listSelector)
@inject(user, "user")
@lift({ latest: "0", newmsg: [] as Array<ISlackUserMessage & ISlackBotMessage> }, "Slack")
export default class Slack extends React.Component<Props, void> {
  public render() {
    return (
      <div className={Style.SLACK}>
        <Title />
        <TextInput />
        <Flex flexGrow={1} flexDirection={"row"}>
          <Messages newmsg={this.props.newmsg} post={this.props.post} user={this.props.user} latest={this.props.latest} />
          <Users user={this.props.user} />
        </Flex>
        </div>
    )
  }
}

详细信息请移步我的博客

高阶用法

因为inject可以接受并订阅你的任意类型数据,所以你可以随意组合你的数据源,也可以组合成高阶数据源(依赖其他数据源的数据源),并把他们作为你视图的数据层。

//组合数据源
didmount() {
  fetch("xxx")
    .then(x => fetch("yyy"))
    .then(yyy => this.setState({yyy}))
}

//数据源订阅数据源
didmount() {
  fetch("xxx")
    .then(xxx => {
      this.setState({xxx})
      return fetch("yyy")
    })
    .then(yyy => this.setState({yyy}))
}

//商品详情 url: xxx/:pid
componentReceiveProps(nextProps) {
  if (this.props.pid !== nextProps.pid) getById(pid).then(data => this.setState({data}))
}

componentDidMount() {
  getById(pid).then(data => this.setState({data}))
}

//websocket
didmount() {
  this.ws = new Websocket(url)

  ws.onmessage = (message) => {
    this.setState(message)
  }
}

willUnmount() {
  ws.close()
}

mengbi.jsÏ

//组合数据源
@inject(() => fetch(xxx).then(xxx => fetch(yyy)), "yyy")
@lift({yyy: null})

//数据源订阅数据源
@inject((currentStore, nextStore) => currentStore.xxx !== nextStore.xxx ? fetch(yyy) : null, "yyy")
@inject(() => fetch(xxx), "xxx")
@lift({xxx: null, yyy: null})

//商品详情 url: xxx/:pid
@inject((currentStore, nextStore) => currentStore.pid !== nextStore.pid ? getById(nextStore.pid) : null, "data")

//websocket(rxjs)
@inject(() => Observable.webSocket(url), "message")Ï

为什么要使用mengbi

  • 帮你去掉了组件生命周期,组件可以写更纯粹的业务(render, handle)
  • 声明式的注入数据源,而不是命令式的dispatch或者手动setState
  • 可订阅state和props和,而react只能订阅props: componentWillReceiveProps
  • 响应式设计,只需要考虑怎么写数据源,组合数据源和触发数据源
  • 跨组件通讯, 比如你有个Dialog组件被lift了,只需要Store.children.Dialog.setState({display: "open"})就能打开这个对话框了

使用上的建议

因为meng是管理数据源为主,夸组件通讯为辅的。你可以像redux那样直接注入数据源,但是应该尽量避免让组件从其他地方获取数据,原则上组件获取数据的方式只有一个,就是父组件, 所有的数据源都应该从视图开始注入,然后传到下面的子组件,这是符合依赖倒置原则的。上面的示例就是一个正确的用法。

其他

发现一款和我思路很像的库,强烈建议看看freactal 发现第二个和我思路很像的库,preact版本,可以说这个库的思路和我基本是完全一样了: wiretie

另附一篇我之前写的文章我为什么不用redux