覆盖兄弟组件的行为

时间:2016-12-07 12:53:53

标签: reactjs redux

我一直在努力克服兄弟组件(没有父子关系的组件)之间的组件行为的概念。

我有一个App组件,可以呈现标题和内容组件。标题包含一个允许用户导航回来的按钮(在本例中不是真实的)。现在我希望内容组件覆盖后退按钮的行为,例如,如果用户正在编辑表单,我想弹出一个模态。

我想这样做的原因是因为我想(可选)控制内容组件本身内的导航。

我找到了这个(参见代码片段)解决方案,但我觉得这不是解决这个问题的正确方法。我想就如何处理这种情况提出一些建议。

一些附注:

  • 我实际上正在构建一个反应原生的应用程序,但为了吸引更多人,我已将其简化为反应示例。我正在使用NavigatorExperimental进行导航。

  • 我使用的是redux / redux-form

感谢任何帮助。

class NavigationHeader extends React.Component {
  render() {
    return (
       <span onClick={this.props.goBack}>Go back!</span>
    );
  }
}

class Content extends React.Component {
  componentDidMount() {
    this.props.setBackButtonBehaviour(() => console.log("BackButton overridden from Content"));
  }
  
  componentWillUnmount() {
     this.props.resetBackButtonBehaviour();
  }
  
  render() {
    return (<div style={{background: "red"}}>Content</div>); 
  }

}

class Navigator extends React.Component {
  constructor(props) {
    super(props);
      
    this.state = {
      overrideHeaderBackButtonBehaviour: null
    }
  }
  
  setBackButtonBehaviour(func) {
      this.setState({overrideHeaderBackButtonBehaviour: func});
  }
    
  resetBackButtonBehaviour() {
       this.setState({overrideHeaderBackButtonBehaviour: null});
  }
    
  defaultBackButtonBehaviour() {
    console.log("Default back-button behaviour");
  }
  
  render() {
    return (
      <div>
        <NavigationHeader goBack={this.state.overrideHeaderBackButtonBehaviour || this.defaultBackButtonBehaviour} />
        <Content
          setBackButtonBehaviour={this.setBackButtonBehaviour.bind(this)} 
          resetBackButtonBehaviour={this.resetBackButtonBehaviour.bind(this)}
    />
      </div>
    );
  }
}

ReactDOM.render(<Navigator />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="app">
</div>

1 个答案:

答案 0 :(得分:0)

您可以尝试这样的事情:

Header.js

import actions from './actions';

@connect(
  // map state to props
  (state, props) => ({
    canNavigate: state.navigation.canNavigate,
  }),
  // map dispatch to props
  {
    goBack: actions.goBack,
    showNavigationAlert: actions.showNavigationAlert,
  }
)
class Header extends React.Component {
  render() {
    return (
      {/* if 'canNavigate' flag is false and user tries to navigate, then show alert */}
      <span
        onClick={this.props.canNavigate ? this.props.goBack : this.props.showNavigationAlert}
      >
          Go back!
      </span>
    );
  }
}

Content.js

import actions from './actions';

@connect(
  // map state to props
  (state, props) => ({
    navigationAlert: state.navigation.navigationAlert,
  }),
  // map dispatch to props
  {
    toggleNavigation: actions.toggleNavigation,
  }
)
class Content extends React.Component {
  render() {
    return (
      <div>
        {/*
            Show modal if 'navigationAlert' flag is true;
            From the modal, if user choose to leave page then call toggleNavigation.
            You may call the corresponding navigation function after this (eg: goBack)
        */}
        {this.props.navigationAlert && <Modal toggleNavigation={this.props.toggleNavigation} />}

        {/* eg: disable navigation when clicked inside the div */}
        <form onClick={this.props.toggleNavigation}>
          Content click!
        </form>
      </div>
    ); 
  }
}

App.js

class App extends React.Component {
  render() {
    return (
      <div>
        <Header />
        <Content />
      </div>
    );
  }
}

navigationReducer.js

const initialState = {
  canNavigate: true,
  navigationAlert: false,
};

export function navigationReducer(state = initialState, action = {}) {
  let newState = {};

  switch (action.type) {
    case TOGGLE_NAVIGATION:
      return { ...state, canNavigate: !state.canNavigate };

    case TOGGLE_NAVIGATION_ALERT:
      return { ...state, navigationAlert: !state.navigationAlert };

    default:
      return state;
  }
}

actions.js

export function toggleNavigation(gdsSession) {
  return { type: TOGGLE_NAVIGATION };
}

export function toggleNavigationAlert(gdsSession) {
  return { type: TOGGLE_NAVIGATION_ALERT };
}

// ... other actions

并渲染成DOM。

ReactDOM.render(<App />, document.getElementById("app"));