前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >react-redux Hook API 简介

react-redux Hook API 简介

作者头像
张宗伟 plus
发布2022-10-28 13:36:24
1.5K0
发布2022-10-28 13:36:24
举报
文章被收录于专栏:张宗伟 plus张宗伟 plus

在跟着redux教程实现Reddit API实例时(参考文章1),想着把类组件用函数组件给改写一下,于是就去看了react-redux的Hook API,最主要就是useSelector、useDispatch和useStore,Hook API让你不必使用connect、mapState和mapDispatch。useSelector需要注意的地方要多一些,文中所有内容均参考react-redux官方教程,就是翻译和总结了一下(参考文章2)。

useSelector


具体形式如下,接收两个参数,第二个参数可选。

代码语言:javascript
复制
useSelector(selector: Function, equalityFn?: Function)

store中的state是selector的唯一参数,可以从redux store中获取数据。

selector应该是一个纯函数,因为它潜在性地会在任意时刻执行多次。

useSelector()还订阅了store,所以除了在函数组件被渲染时会被调用,当每次dispatch action时也会被调用。

selector可以返回任何值,不一定如mapState一样是个对象。而且这个返回值即是useSelector()的返回值。

当dispatch action后useSelector()会将之前的返回值和现在的返回值进行浅比较,注意使用的是reference equality ===来比较的,而connect是使用shallow equality ==来比较的,如果相等的话就不更新UI,如果不相等就强制更新UI。

如果在一个函数组件中调用了多次useSelector(),就会生成多个独立的对store的订阅,但是因为react的批量更新机制,当每次dispatch action时,还是只返回一个新值。

注意不要用useSelector()中的selector以整个对象的形式返回store state,因为每次返回的都是一个新对象,依据第五条的比较方式来说,肯定会重新触发更新的,造成不必要的性能浪费。所以要使用多个useSelector()去分别获取store中的state,或者使用第二个参数。

selector无法访问自身的props(这里我认为是selector内部无法获取),但是可以通过闭包或者a curried selector取得。

代码语言:javascript
复制
export const TodoListItem = props => {
  // 以下就是闭包形式的获取
  const todo = useSelector(state => state.todos[props.id])
  return <div>{todo.text}</div>

当使用memoizing selector时需格外小心。这一点还没有理解清楚,对我来说还是有点儿难的。

useDispatch


和dispatch一样,用于触发action。需要注意的是,当将触发函数通过props传入到子组件中,在子组件中触发时,要使用callback Hook以避免不必要的渲染。

useStore


获取整个store,但是并不会订阅store的变化,所以当dispatch action时,不会自动更新。

useAction


不常用

useShallowEqualSelector


不常用

Reddit API 具体实例


需要注意的是,两者代码行数的变化。

类组件形式

代码语言:javascript
复制
class AsyncApp extends Component {
  componentWillReceiveProps(nextProps) {
    const { selectSubreddit } = nextProps
    const { dispatch } = this.props
    // 针对handleChange函数,当selectSubreddit一样时,不需要dispatch action    
    if (selectSubreddit !== this.props.selectSubreddit) {
      dispatch(fetchPostsIfNeeded(selectSubreddit))
    }
  }
  componentDidMount() {
    const { dispatch, selectSubreddit } = this.props
    dispatch(fetchPostsIfNeeded(selectSubreddit))
  }
  handleChange(subreddit) {
    const { dispatch } = this.props
    dispatch(selectSubreddit(subreddit))
  }
  handleRefreshClick(subreddit) {
    const { dispatch } = this.props
    dispatch(invalidateSubreddit(subreddit))
    // 刷新时,必须要dispatch action
    dispatch(fetchPostsIfNeeded(subreddit))
  }
  render() {
    const { selectSubreddit, isFetch, lastUpdate, posts } = this.props
    return (
      <div>
        <Picker
          value={selectSubreddit}
          onChange={(msg) => this.handleChange(msg)}
          options={['reactjs', 'frontend']}
        />
        <p>
          {
            lastUpdate &&
            <span>Last Updated at: {new Date(lastUpdate).toLocaleTimeString()}</span>
          }
        </p>
        {
          !isFetch &&
          <button onClick={() => this.handleRefreshClick(selectSubreddit)}>reFresh</button>
        }
        {isFetch && <h2>Loading...</h2>}
        {
          posts.length ?
            <Posts posts={posts} /> :
            <h2>Empty</h2>
        }
      </div>
    )
  }
}
const mapStateToProps = (state) => {
  const { selectSubreddit, postsBySubreddit } = state
  const { isFetch, didInvalidate, lastUpdate, items } = postsBySubreddit[selectSubreddit] || {
    isFetch: true,
    items: []
  }
  return {
    selectSubreddit,
    isFetch,
    didInvalidate,
    lastUpdate,
    posts: items
  }
}
export default connect(mapStateToProps)(AsyncApp);

函数组件形式

代码语言:javascript
复制
const AsyncApp = memo(() => {
  const subreddit = useSelector((state) => state.selectSubreddit)
  const { 
    isFetch, 
    lastUpdate, 
    didInvalidate,
    items:posts 
  } = useSelector((state) => state.postsBySubreddit[subreddit]) || {
    isFetch: true,
    items: []
  }
  const dispatch = useDispatch()
  useEffect(() => {
    dispatch(fetchPostsIfNeeded(subreddit))
  // 第二个参数同样是针对handleChange,当subreddit一样,则不dispatch action    
  },[subreddit])
  function handleChange(subreddit) {
    dispatch(selectSubreddit(subreddit))
  }
  function handleRefreshClick() {
    dispatch(invalidateSubreddit(subreddit))
    // 刷新时必须要dispatch action    
    dispatch(fetchPostsIfNeeded(subreddit))
  }
  return (
    <div>
        <Picker
          value={subreddit}
          onChange={handleChange}
          options={['reactjs', 'frontend']}
        />
        <p>
          {
            lastUpdate &&
            <span>Last Updated at: {new Date(lastUpdate).toLocaleTimeString()}</span>
          }
        </p>
        {
          !isFetch &&
          <button onClick={handleRefreshClick}>reFresh</button>
        }
        {isFetch && <h2>Loading...</h2>}
        {
          posts.length ?
            <Posts posts={posts} /> :
            <h2>Empty</h2>
        }
      </div>
  )
})
export default AsyncApp

参考文章

  1. http://cn.redux.js.org/docs/advanced/ExampleRedditAPI.html
  2. https://react-redux.js.org/api/hooks
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-01-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 张宗伟 plus 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档