Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于useSelector等redux hooks API无法在H5环境使用的临时解决办法 #4981

Closed
calimanco opened this issue Dec 1, 2019 · 10 comments
Assignees

Comments

@calimanco
Copy link

问题描述

几个月前就一个有人提起useSelector等redux hooks API无法在H5环境使用的问题#3447
但一直都是“解决中”的状态。苦等官方未果,但项目又不等人,只能自己动手用自定义hooks实现了相同的API,后面切换到官方实现也方便,在项目上跑表现良好。
这里分享给遇到相同问题的朋友,也当变向催官方改了。(笑)
欢迎批评指正。

代码

import { useRef, useState, useCallback, useEffect, useMemo } from '@tarojs/taro'
import { createStore } from 'redux'

// 这里就是Redux createStore后返回的实例,具体封装因人而异。一般都会写到configStore文件里。
const globalStore = createStore(...)

/**
 * 允许你使用 selector 函数从一个 Redux Store 中获取数据
 * @param selector 相对于mapStateToProps函数
 * @param equalityFn 自定义比较数据是否前后一致的函数,true则不更新,false则更新
 */
export function useSelector(selector: Function, equalityFn?: Function) {
  const unsubscribe = useRef(null as any)
  const preState = useRef(globalStore.getState())
  const [result, setResult] = useState(selector(preState.current))

  const updateStore = useCallback(() => {
    const newState = globalStore.getState()
    const preResult = selector(preState.current)
    preState.current = newState
    const newResult = selector(newState)
    if (equalityFn) {
      if (!equalityFn(preResult, newResult)) {
        setResult(newResult)
      }
    } else if (JSON.stringify(preResult) !== JSON.stringify(newResult)) {
      // 这里未实现复杂的diff算法,这种比较方法处理循环引用会报错,需要注意。
      setResult(newResult)
    }
  }, [equalityFn, selector])

  useEffect(() => {
    unsubscribe.current = globalStore.subscribe(() => updateStore())

    return () => {
      unsubscribe.current()
    }
  }, [updateStore])

  return result
}

/**
 * 这个Hook返回Redux store的dispatch引用。你可以使用它来 dispatch actions。
 */
export function useDispatch() {
  return useCallback((action) => {
    globalStore.dispatch(action)
  }, [])
}

/**
 * 返回一个store引用。
 */
export function useStore() {
  return useMemo(() => {
    return globalStore
  }, [])
}

注意

我没有在useSelector里实现复杂的diff算法,这里可以自行修改成需要的实现。
把这段改掉就行。
JSON.stringify(preResult) !== JSON.stringify(newResult)

@calimanco calimanco added the H5 label Dec 1, 2019
@taro-bot
Copy link

taro-bot bot commented Dec 1, 2019

CC @Littly

@taro-bot
Copy link

taro-bot bot commented Dec 1, 2019

欢迎提交 Issue~

如果你提交的是 bug 报告,请务必遵循 Issue 模板的规范,尽量用简洁的语言描述你的问题,最好能提供一个稳定简单的复现。🙏🙏🙏

如果你的信息提供过于模糊或不足,或者已经其他 issue 已经存在相关内容,你的 issue 有可能会被关闭。

Good luck and happy coding~

@dpyzo0o
Copy link

dpyzo0o commented Dec 16, 2019

请问楼主用这个方法在开发过程中有没有碰到什么问题?比如官方回答的路由需要特殊处理这一块#3447 (comment)

@calimanco
Copy link
Author

请问楼主用这个方法在开发过程中有没有碰到什么问题?比如官方回答的路由需要特殊处理这一块#3447 (comment)

我这个是应用层面的实现,只是模拟了相同的接口,还是跟官方底层实现无关,理论上只要我引用的那几个官方接口没问题,就不会有问题。我只是在H5和微信小程序环境用,其他环境请自行测试。
具体我也没看官方源码,不清楚特殊处理是指啥。按照redux的原理来说,不应该有啥关联才对。

@zhangxiangliang
Copy link

@calimanco 这具体是怎么操作,我直接写到 store 里运行 h5 后会出现 store.subscribe is not a function 的错误。

@calimanco
Copy link
Author

@calimanco 这具体是怎么操作,我直接写到 store 里运行 h5 后会出现 store.subscribe is not a function 的错误。

我不清楚的是store变量是指啥,请确保你的store是createStore返回的实例。

我的使用方法是使用这样的目录结构:
|--store
|----configstore.ts redux的初始化设置
|----index.ts 导出实例供其他模块使用
|----tools.ts 与redux相关的一些工具函数

我在configstore导出的是封装了createStore的初始化函数,在index里引用并调用,再导出hooks的方法。其他模块使用就是直接引用index里面的hooks。
请注意我这里是在index里完成redux初始化的,也就是代码里的globalStore,这也符合redux唯一数据源的原则。

@zhangxiangliang
Copy link

@calimanco 我的意思是添加了 export const globalStore = createStore(rootReducer, enhancer);,然后再这个文件里直接用你的代码。小程序端编译是没问题的,但是再 h5编译会出现 store.subscribe is not a function

话说你的 Taro 版本号是?

@calimanco
Copy link
Author

@calimanco 我的意思是添加了 export const globalStore = createStore(rootReducer, enhancer);,然后再这个文件里直接用你的代码。小程序端编译是没问题的,但是再 h5编译会出现 store.subscribe is not a function

话说你的 Taro 版本号是?

我从1.3.23到现在最新的1.3.32都在用这块代码,并没有问题。
请一步步调试看看,是不是哪里在createStore调用之前就使用了hooks的方法。

随便一提,如果你原样复制我的代码,我并没有使用store.subscribe,报错也应该是globalStore.subscribe吧。

@zhangxiangliang
Copy link

zhangxiangliang commented Dec 25, 2019

@calimanco

因为报错显示的是 b.subscribe 我只是为了方便你看,就顺手写成了 store。

src/store/index.ts

export let globalStore: any = createStore(rootReducer, enhancer);
//  后面都是你的源码

src/app.tsx

import {globalStore} from './store'

class App extends Component {
  //...
  render () {
    return (
      <Provider store={globalStore}>
        <HomeIndex />
      </Provider>
    )
  }
  //...
}

我全局搜索了我并没有调用过函数,但是却还是报错了。

image
image

@zhangxiangliang
Copy link

zhangxiangliang commented Dec 25, 2019

@calimanco 我傻了,我知道那里有问题了

@Chen-jj Chen-jj closed this as completed Jul 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants