我正在使用react,react-router&终极版。我的应用程序的结构是这样的:
CoreLayout
-> <MaterialToolbar /> (contains back button)
-> {children} (react-router)
当用户按下后退按钮(通常由CoreLayout处理)时,我希望当前子组件处理后退按钮而不是父按钮。 (在我的情况下,我希望当前视图检查其数据是否已被修改,并在实际返回之前弹出“你确定要取消吗?”框。)如果孩子不想处理这个问题,父母会这样做。
另一个例子是允许childview
在工具栏中设置标题。
我的阅读告诉我,通过ref访问组件并在其上调用方法不是反应方式 - 由于我使用redux-connect,这也变得有点困难。实现此行为的正确方法是什么?
答案 0 :(得分:0)
我就是这样做的,假设你的意思是导航后退按钮(而不是浏览器后退按钮):
class CoreLayout extends Component {
handleBack () {
//... use router to go back
}
render () {
return <div>
<MaterialToolbar />
{React.children.map(this.props.children, child => React.cloneElement(child, { onBack: this.handleBack }))}
</div>
}
}
class Child extends Component {
handleBackButtonClick () {
// Here perform the logic to decide what to do
if (dataHasBeenModifiedAndConfirmed) {
// Yes, user wants to go back, call function passed by the parent
this.props.onBack()
} else {
// User didn't confirm, decide what to do
}
}
render () {
return <div onClick={this.handleBackButtonClick.bind(this)}>
Go Back
</div>
}
}
您只需通过props将一个函数从父级传递给子级。然后在子代中,您可以实现逻辑以检查是否确实要将工作委托给父组件。
由于您使用react-router并且您的孩子通过this.props.children
传递给您的父组件,要传递onBack
功能,您需要映射孩子并使用React.cloneElement
通过您的道具(如果您需要更多详细信息,请参阅此答案:React.cloneElement: pass new children or copy props.children?)。
修改强>
因为看起来你想让孩子们决定,你可以这样做(使用refs):
class CoreLayout extends Component {
constructor () {
super()
this.childRefs = {};
}
handleBack () {
for (let refKey in Object.keys(this.childRefs) {
const refCmp = this.childRefs[refKey];
// You can also pass extra args to refCmp.shouldGoBack if you need to
if (typeof refCmp.shouldGoBack === 'function' && !refCmp.shouldGoBack()) {
return false;
}
}
// No child requested to handle the back button, continue here...
}
render () {
return <div>
<MaterialToolbar />
{React.children.map(this.props.children, (child, n) => React.cloneElement(child, {
ref: cmp => { this.childRefs[n] = cmp; }
}))}
</div>
}
}
class Child extends Component {
shouldGoBack () {
// Return true/false if you do/don't want to actually go back
return true
}
render () {
return <div>
Some content here
</div>
}
}
通常情况下,使用React它会更容易/更容易拥有一个&#34; smart&#34;根据状态决定的父级,但考虑到你的具体情况(父级的后退按钮和子级中的逻辑)并且没有重新实现其他一些东西,我认为使用ref这种方式很好。
或者(使用Redux)作为建议的另一个答案,您需要从可以在父级中使用的子级设置Redux状态的某些东西来决定要做什么。
希望它有所帮助。
答案 1 :(得分:0)
我认为没有正确的方法来解决这个问题,但有很多种方式。如果我正确理解您的问题,大多数情况下后退按钮onClick
处理程序将在CoreLayout
内处理,但是当呈现特定子项时,该子项将处理onClick
事件。这是一个有趣的问题,因为更改后退按钮功能的能力需要全局可用,或至少在CoreLayout
和特定子组件中可用。
我没有使用过redux,但是我使用过Fluxible,并熟悉Flux架构和pub / sub模式。
也许您可以利用您的redux商店来确定后退按钮的功能。并且您的CoreLayout
组件将处理呈现提示。下面的代码有一个错误,但我想我不会删除我的答案,以便让你知道我在说什么,希望下面的代码可以做到这一点。您需要仔细考虑逻辑才能使其正常工作,但这个想法就在那里。使用商店确定后退按钮的功能。
//Core Layout
componentDidMount() {
store.subscribe(() => {
const state = store.getState();
// backFunction is a string correlating to name of function in Core Layout Component
if(state.backFunction) {
// lets assume backFunction is 'showModal'. Execute this.showModal()
// and let it handle the rest.
this[state.backFunction]();
// set function to false so its not called everytime the store updates.
store.dispatch({ type: 'UPDATE_BACK_FUNCTION', data: false})
}
})
}
showModal() {
// update state, show modal in Core Layout
if(userWantsToGoBack) {
this.onBack();
// update store backFunction to be the default onBack
store.dispatch({ type: 'UPDATE_BACK_FUNCTION', data: 'onBack'})
// if they don't want to go back, hide the modal
} else {
// hide modal
}
}
onBack() {
// handle going back when modal doesn't need to be shown
}
下一步是在子组件安装时更新您的商店
// Child component
componentDidMount(){
// update backFunction so when back button is clicked the appropriate function will be called from CoreLayout
store.dispatch({ type: 'UPDATE_BACK_FUNCTION', data: 'showModal'});
}
这样您就不必担心将任何函数传递给子组件,而是让商店的状态决定调用哪个函数{。}}。