【问题标题】:How Exactly useselector works?究竟 useselector 是如何工作的?
【发布时间】:2021-05-04 11:39:05
【问题描述】:

根据文档

useSelector(selector: Function, equalityFn?: Function)

useSelector 接受两个参数,第二个是可选的,因为默认情况下它比较严格 === 引用相等检查,但不比较浅相等。

  const state = useSelector(state => {
        console.log("useSelector rerendering");
        return state.counter
    }, shallowEqual)

另一个是

  const state = useSelector(state => {
        console.log("useSelector rerendering");
        return state.counter
    })

在这两种情况下,当 redux 存储状态发生变化时,组件会重新渲染,并且当本地状态发生变化时,它将渲染(在 useSelector 中打印 console.log)

我真的不明白它是如何工作的

完整的源代码

import React, { useState } from 'react'
import { shallowEqual, useDispatch, useSelector } from 'react-redux'
import { decrement, increment } from './store/actions'


export default function CounterHooks(props) {

    const [submit, setSubmit] = useState(false)
    const state = useSelector(state => {
        console.log("useSelector rerendering");
        return state.counter
    }, shallowEqual)


    const dispatch = useDispatch()
 
    console.log("component rerendering");
    const increments = () => {

        dispatch(increment());

    }

    const decrements = () => {

        dispatch(decrement());
    }
    const submitButton = () => {
        console.log("component submit", submit);

        setSubmit((previousState) => !previousState)

    }
    return (
        <div>
            <button onClick={increments} >Incrmeent Counter</button>
            <br />
            <button onClick={decrements} >Decrement Counter</button>
            <br />
            <button onClick={submitButton} >Submit</button>
            <br />

            <h2>total : {state.count}</h2> <br />
            <h2>Submit:{String(submit)}</h2> <br />
           
           
        </div>
    )
}

我的问题是第二个参数究竟是如何工作的?

【问题讨论】:

  • 您的实际问题是什么?这里的行为是否与您想象的不同?
  • @phry.第二个参数的工作原理。我看不出有什么区别

标签: reactjs redux react-redux


【解决方案1】:

在你的例子中,它没有任何区别。

shallowEquals 在您选择内容可能相似但引用不同的对象时才有意义。

查看这两个对象:

const a = { foo: "bar" }
const b = { foo: "bar" }

console.log( a === b ) // will log false
console.log( shallowEquals(a, b)) // will log true

虽然a 和b 是两个具有相似形状和内容的对象,但它们不是同一个对象。 现在shallowEquals 在a.foo 和b.foo 之间进行=== 比较,因为两者都是具有相同内容的字符串,所以a.foo === b.foo 将是正确的。

如果你在选择器中创建一个新对象,这确实会起作用,比如说

const result = useSelector((state) => {

  return { a: state.foo.bar, b: state.baz.boo }

})

这样做的结果将始终是一个新对象,因此默认情况下useSelector 将始终假定它们是不同的,即使state.foo.bar 和state.baz.boo 实际上没有改变。 如果您使用shallowEqual,useSelector 将查看对象的直接(仅 1 级深度)子属性并进行比较。然后它会注意到它们实际上是相等的而不是重新渲染。

【讨论】:

  • @phry。感谢您为回答所做的努力。根据您的回答,如果状态对象值没有改变,那么它不会重新渲染。但出于测试目的,我返回了当前状态,而不是像这样递增case INCREMENT: return { ...state, count: state.count } 但即使在添加浅相等后它仍然会重新渲染
  • @phry.is it possible to give real world example for this use case in react
  • 上面的是一个真实世界的用例,只要你用user替换foo,用bar替换firstName,用@987654341替换baz @ 和 boo 和 token。但是还有数百万其他用例。但实际上:在大多数情况下,只需从状态中选择单个属性并使用多个 useSelector。你几乎永远不需要 shallowEqual 那样,它更像是一个逃生舱口。
【解决方案2】:

将以下内容视为减速器,

const userSlice = (
{name: "user", initialState: {name: "test", isLoggedIn: true}, 
reducers: {updateUser(state, action) => (state.isLoggedIn=action.payload["isLoggedIn"])}
}

如果我们向 reducer 发送具有相同数据的操作,如下所示,

const result = useSelector(state => state.users)
dispatch({isLoggedIn: true})

上面的代码不会导致组件重新渲染,因为我们引用的 state.users 和之前的一样。

我们可以通过以下更改重新渲染,

    const result = useSelector(state => ({name: state.users.name, isLoggedIn: state.users.isLoggedIn)
dispatch({isLoggedIn: true})

在上面的代码中,我们直接返回了一个对象,所以每当 useSelector 运行时,由于引用相等,返回的对象不会相同。这使得组件重新渲染

【讨论】:

    猜你喜欢
    • 2011-06-26
    • 2021-08-15
    • 2012-06-08
    • 2011-10-11
    • 2013-07-05
    • 1970-01-01
    • 2014-09-29
    • 2011-06-01
    • 1970-01-01
    相关资源
    最近更新 更多