为什么无论状态是对象还是字符串,React 的重新渲染方式都不同?

IT技术 reactjs
2021-05-05 20:41:23

下面的 React 示例,userProfile状态是一个对象
第一次渲染:状态是{}
第二次渲染:状态是res.data
-> 无限重新渲染

如果userProfile更改为一个字符串,useState('')setUserProfile(res.data.bio)
1渲染:状态是''
第二渲染:状态是res.data.bio
第三渲染:状态res.data.bio
- >停止第三后重新呈现

两个问题:

  • 为什么 React 会根据userProfile状态是对象还是字符串而不同地重新渲染
  • 什么时候userProfile是一个字符串,为什么 React 在第三次渲染后停止重新渲染而不是在第二次渲染之后?

应用程序.js

import UserProfile from './UserProfile';

const App = () => {
    const login = 'facebook';
    
    return (
        <Router>
            <Link to={`/user/${login}`}>Fetch Facebook bio from GitHub</Link>
            <Route path='/user/:login' component={UserProfile} />
        </Router>
    );
};

用户配置文件.js

const UserProfile = ({ match }) => {
    const [userProfile, setUserProfile] = useState({});

    const getUser = async username => {
        const url = `https://api.github.com/users/${username}`;
        const res = await axios.get(url);
        setUserProfile(res.data);
    };

    getUser(match.params.login);
// intentionally not using useEffect(() => { getUser(match.params.login); }, []);

    return (<p>{userProfile.bio}</p>);
};
1个回答

React 检查状态变量是否已更改,如果已更改,则重新渲染。因为无论你比较两个对象多少次(即使它们看起来相同),它们总是不同的,React 会不断地重新渲染。

另一方面,字符串是原始类型,因此比较不同。试试这个:

console.log({d: 3} === {d: 3})
console.log("sss" === "sss")

它会让您了解为什么会发生这种情况。

因此,即使您不断将状态变量设置为同一个对象,它也并非完全相同。但是一个字符串是相同的,所以它停止重新渲染。

查看这篇关于 JS 中对象相等性的文章:http : //adripofjavascript.com/blog/drips/object-equality-in-javascript.html

现在让我回答你的第二个问题:

当 userProfile 是一个字符串时,为什么 React 在第三次渲染而不是第二次渲染后停止重新渲染?

如果您查看React Docs,您会遇到以下 2 段,我相信可以回答您的问题:

如果您将 State Hook 更新为与当前状态相同的值,React 将退出而不渲染子项或触发效果。(React 使用 Object.is 比较算法。)

请注意,在退出之前,React 可能仍需要再次渲染该特定组件。这不应该是一个问题,因为 React 不会不必要地“深入”到树中。如果您在渲染时进行昂贵的计算,您可以使用 useMemo 优化它们

注意第二段。