为何CSS过渡在函数调用组件时生效,组件标签调用时失效?
问题:组件标签形式调用的侧边栏过渡效果失效
我实现了一个带有展开/收起过渡效果的侧边栏。当以函数形式调用Sidebar组件时(示例中的侧边栏1),过渡效果正常运行;但以组件标签形式调用该组件时(示例中的侧边栏2),过渡效果完全失效。请问示例2中的过渡效果为何无法生效?
代码示例
JavaScript
const App = (props) => { const [sidebarCollapsed, setSidebarCollapsed] = React.useState(false); const Sidebar = ({ bg, ...props }) => { const width = sidebarCollapsed ? "0px" : "200px"; return ( <div style={{ backgroundColor: bg, height: "100px", width: width, transitionDuration: "500ms" }} /> ); }; return ( <div> <div> {/* Sidebar 1 -> function */} {Sidebar({ bg: "blue" })} {/* Sidebar 2 -> component */} <Sidebar bg="red" /> <button onClick={() => setSidebarCollapsed(!sidebarCollapsed)}> Toggle sidebar </button> </div> </div> ); }; ReactDOM.render(<App />, document.getElementById("root"));
HTML
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <div id="root"></div>
原因分析
核心问题出在**Sidebar组件定义在App组件内部**:
- 函数形式调用
Sidebar({ bg: "blue" }):本质是执行普通函数,返回的DOM元素会被React直接复用、更新,状态变化时能触发正常的CSS过渡。 - 组件标签形式
<Sidebar bg="red" />:React会将其识别为组件,但App每次因sidebarCollapsed状态变化重渲染时,都会重新定义一遍Sidebar函数。React判断组件类型时会认为这是一个全新组件,于是卸载旧的<Sidebar>实例,重新挂载新实例——新实例直接渲染目标宽度(0或200px),跳过了过渡需要的渐变过程,自然没有动画效果。
解决方法
方法1:将Sidebar移到App外部定义
保证组件引用在App重渲染时不会变化:
const Sidebar = ({ bg, collapsed }) => { const width = collapsed ? "0px" : "200px"; return ( <div style={{ backgroundColor: bg, height: "100px", width: width, transitionDuration: "500ms" }} /> ); }; const App = (props) => { const [sidebarCollapsed, setSidebarCollapsed] = React.useState(false); return ( <div> <div> {Sidebar({ bg: "blue", collapsed: sidebarCollapsed })} <Sidebar bg="red" collapsed={sidebarCollapsed} /> <button onClick={() => setSidebarCollapsed(!sidebarCollapsed)}> Toggle sidebar </button> </div> </div> ); }; ReactDOM.render(<App />, document.getElementById("root"));
方法2:用useCallback缓存内部组件
如果必须在App内部定义Sidebar,可以通过useCallback固定组件函数的引用:
const App = (props) => { const [sidebarCollapsed, setSidebarCollapsed] = React.useState(false); const Sidebar = React.useCallback(({ bg }) => { const width = sidebarCollapsed ? "0px" : "200px"; return ( <div style={{ backgroundColor: bg, height: "100px", width: width, transitionDuration: "500ms" }} /> ); }, [sidebarCollapsed]); return ( <div> <div> {Sidebar({ bg: "blue" })} <Sidebar bg="red" /> <button onClick={() => setSidebarCollapsed(!sidebarCollapsed)}> Toggle sidebar </button> </div> </div> ); };
内容的提问来源于stack exchange,提问作者Ignace Vau
相关产品推荐
相关产品推荐

