简体   繁体   中英

react, redux - Modifying parent components with redux

Okay so I had a problem when programming in react, and I've found that it's a common one. If I have multiple nested components, in my case I have:

<AppView>
  <Navigation/> // this is a navbar
  <ViewHandler currentTab={props.currentTab}/>
  <Footer/>
</AppView>

And then in <ViewHandler/> I have other dumb presentational components, which also have nested components as well. If I have a button in a deeply nested component within <ViewHandler> , and I want to respond to onClick from that button by changing something many parent components above the component that I am in, how would I do so? In my case I would be reacting to the button being clicked in that deeply nested component, and then I want to change the selected tab on <Navigation> . I don't want to pass a bunch of callback functions down as properties, because that feels very scotch-tape-ish.

I learned redux because I read that it solved this problem. But for me it hasn't. I am giving <AppView> access to my redux store using react-redux's <Provider> , and I can access the store through props ( props.currentTab ). But for all the components nested within <AppView> , they don't have access to the store or any of my action creators. How can modify my store from within a deeply nested component so that I may change a parent component without passing a ton of callback functions down? Or is this just incorrect architecture? I thought redux would solve this problem but it hasn't.

Yes I have connected my component. I just don't like the idea of passing down store.state information as props because it gets very redundant with many nested components.

I don't know why you think you have to send props all the way down your component tree. That's what connect and mapStateToProps help you avoid: they let you turn bits of app state into props only for the components which need it.

in your button's onClick handler, create and dispatch a Redux action:

// button.js

onClick={() => {
  dispatch({
    payload: 1 // or whatever value
    type: 'SET_SELECTED_TAB'
  });
}}

next, have your reducer function watch for this action and modify a bit of Redux app state:

// reducer.js

if (action.type === 'SET_SELECTED_TAB') {
  return {
    ...currentAppState,
    selectedTab: action.payload
  };
}

finally, in the render function of your <Navigation> component, you decide which tab to show based on the current values in that bit of app state:

// Navigation.js

render() {
  return (
    <div>
      current tab: {this.props.selectedTab}
    </div>
  );
}

access to that state is via connect and mapStateToProps :

// Navigation.js still

const mapStateToProps = (appState) => {
  return {
    selectedTab: appState.selectedTab
  };
};

export default connect(mapStateToProps)(Navigation);

Hoc (higher order components) is a wrapper that is serving methods and data to the children components, usually it's a good idea to use it , but it enforces some 'discipline'.

Example: if your HOC is at level 0 and you have a deeply nested button component at level 4 that calls a method in this same HOC , What should you do ? pass it down the to all 4 levels? the answer is NO WAY !

Because doing so will bring the spaghetti to it , Everytime you click this button , and assuming the method binded to it will mess with the state (internal or the store itself) it will rerender all the 4 levels , and you could avoid that by using the shouldComponentUpdate() but this is way too much work for nothing useful.

So the solution would be to connect every component with mapStateToProps and mapDispatchToProps , right ?

well kind of , in fact after using extensively react and redux , you will notice that for every component , there is a sweet spot in terms of size , childrens , and what you should put in it and what you should not.

Example: you have a button inside a form that controls the send mechanism , there's no need to make a component for the button , it will add up complexity without any benefit. just put it on the form component and you will have both ready to use.

If you really need to call actions or to pass props between a deeply nested component and an HOC then use the connect module at the component level (for your case the button) , but not much because it will make your components heavier (to load and to display).Here are some tips to help :

  • you need to be as specfic as possible when you use mapStateToProps , don't return the whole store , just the piece of data needed , same for mapDispatchToprops , just bind the method that you will be using nothing else.

  • in your case the button doesn't have to know which tab is selected , so a mapDispatchToProps is enough.

  • avoid deep nesting components that handles some kind of logic ,refactor your structure or create A HOC for that component , logic less components in contrary can be nested deeply

  • If you are writing a huge app with a lot of reducers and states , consider using selectors , and some libraries like reselect.

I know that this is not the answer you were expecting but following this guideline will saves you countless hours of refactoring.

Hope it helps

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM