我对React感到沮丧,因为视图层次结构破坏了我的动画。
我有一个<Layout>
组件,用于设置制表符 - 导航布局,其子项放置在布局的内容部分中。它是一个纯组件,具有title
,setTab
和currentTab
的道具。
如果我从视图层次结构角度和单向数据流中考虑这个问题,我会将布局设置为每个视图的子项,以便视图可以设置标题,当前选项卡和setTab
可以委托给其父母。
这基本上就是我在做的事情:
var A = React.createClass({
propTypes: {
setTab: React.PropTypes.func.isRequired
},
render: function() {
return (
<Layout title="A Page" currentTab="A" setTab={this.props.setTab}>
This is A
</Layout>
)
}
});
现在你可以使用React Router或其他东西作为选项卡,但是现在,我只有一个简单的顶级组件切换选项卡及其状态:
var App = React.createClass({
getInitialState: function() {
return {
currentTab: 'A'
}
},
setTab: function(name) {
this.setState({currentTab:name})
},
render: function() {
var component = false
if (this.state.currentTab == 'A') {
return <A setTab={this.setTab}/>
} else {
return <B setTab={this.setTab}/>
}
}
});
在您想要开始添加动画之前,这一切都很好用。
在我的<Layout>
组件中,我有一个高亮显示区块,我想在标签之间切换时从左到右动画。
<div className="tabbar">
<div className={"highlight " + this.props.currentTab}></div>
<div className="tabs">
<div className="tab" onClick={() => {this.props.setTab('A')}}>A</div>
<div className="tab" onClick={() => {this.props.setTab('B')}}>B</div>
</div>
</div>
这是该动画的CSS:
.highlight {
border-bottom: 3px solid red;
width: 50%;
position: absolute;
top: 0;
bottom: 0;
transition: left .25s ease-in-out;
}
.highlight.A {
left: 0;
}
.highlight.B {
left: 50%;
}
我也尝试使用React.addons.CSSTransitionGroup
设置版面标题的动画,但我认为问题是相同的:
这里的问题似乎是<Layout>
组件每次都被完全重新渲染,因为渲染树的根(<A/>
和<B/>
)正在发生变化。
现在的问题是,如何反转视图层次结构,以便<Layout>
不会重新渲染,以便动画可以工作?
注意:在一个不那么做作的例子中,布局也可以从<A/>
获得其他道具,例如onClick处理程序。
这是一个证明我沮丧的JSFiddle:
答案 0 :(得分:0)
确定。我觉得有些东西可行,但我不确定这是不是最好的做法,所以在得出结论这是答案之前,我真的很感激一些反馈。
我通过组件层次结构提出了stitch
作为拼接函数的方法的概念。这将允许我从子组件设置父组件的状态。
function stitch() {
return {
to: (f) => {
this.func = f
},
from: () => {
this.func.apply(null, arguments)
}
}
}
所以现在我的<Layout>
看起来像这样:
var Layout = React.createClass({
propTypes: {
currentTab: React.PropTypes.string.isRequired,
onTab: React.PropTypes.func.isRequired,
stitchTitle: React.PropTypes.string.isRequired
},
getInitialState: function() {
return {
title: ''
}
},
componentWillMount: function() {
this.props.stitchTitle((title) => {
this.setState({title})
})
},
// ...
}
视图如下:
var A = React.createClass({
propTypes: {
setTitle: React.PropTypes.func.isRequired
},
componentWillMount: function() {
this.props.setTitle('Page A')
},
//...
}
我们所要做的就是将它们一起缝合。
var App = React.createClass({
getInitialState: function() {
return {
currentTab: 'A'
}
},
setTab: function(name) {
this.setState({currentTab:name})
},
componentWillMount: function() {
this.titleStitch = stitch()
},
render: function() {
var view = false
if (this.state.currentTab == 'A') {
view = <A setTitle={this.titleStitch.from}/>
} else {
view = <B setTitle={this.titleStitch.from}/>
}
return (
<Layout currentTab={this.state.currentTab} onTab={this.setTab} stitchTitle={this.titleStitch.to}>
{view}
</Layout>
)
}
});
确实有效!这是JSFiddle: