infinite Render in React

后端 未结 3 1973
隐瞒了意图╮
隐瞒了意图╮ 2020-12-20 22:17

I am having problem figuring out why my application is doing endless render.

Inside, My stateful component, I am calling a redux action in componentDidMount method (

3条回答
  •  甜味超标
    2020-12-20 22:57

    Like izb mentions, the root cause of the pb is the business call that is done on a pure component whereas it is just loaded. It is because your component make a business decision (<=>"I decide when something must be showed in myself"). It is not a good practice in React, even less when you use redux. The component must be as stupid a possible and not even decide what to do and when to do it.

    As I see in your project, you don't deal correctly with component and container concept. You should not have any logic in your container, as it should simply be a wrapper of a stupid pure component. Like this:

    import { connect, Dispatch } from "react-redux";
    import { push, RouterAction, RouterState } from "react-router-redux";
    import ApplicationBarComponent from "../components/ApplicationBar";
    
    export function mapStateToProps({ routing }: { routing: RouterState }) {
        return routing;
    }
    
    export function mapDispatchToProps(dispatch: Dispatch) {
        return {
            navigate: (payload: string) => dispatch(push(payload)),
        };
    }
    const tmp = connect(mapStateToProps, mapDispatchToProps);
    export default tmp(ApplicationBarComponent);
    

    and the matching component:

    import AppBar from '@material-ui/core/AppBar';
    import IconButton from '@material-ui/core/IconButton';
    import Menu from '@material-ui/core/Menu';
    import MenuItem from '@material-ui/core/MenuItem';
    import { StyleRules, Theme, withStyles, WithStyles } from '@material-ui/core/styles';
    import Tab from '@material-ui/core/Tab';
    import Tabs from '@material-ui/core/Tabs';
    import Toolbar from '@material-ui/core/Toolbar';
    import Typography from '@material-ui/core/Typography';
    import AccountCircle from '@material-ui/icons/AccountCircle';
    import MenuIcon from '@material-ui/icons/Menu';
    import autobind from "autobind-decorator";
    import * as React from "react";
    import { push, RouterState } from "react-router-redux";
    
    const styles = (theme: Theme): StyleRules => ({
      flex: {
        flex: 1
      },
      menuButton: {
        marginLeft: -12,
        marginRight: 20,
      },
      root: {
        backgroundColor: theme.palette.background.paper,
        flexGrow: 1
      },
    });
    export interface IProps extends RouterState, WithStyles {
      navigate: typeof push;
    }
    
    @autobind
    class ApplicationBar extends React.PureComponent {
      constructor(props: any) {
        super(props);
        this.state = { anchorEl: undefined };
      }
      public render() {
    
        const auth = true;
        const { classes } = this.props;
        const menuOpened = !!this.state.anchorEl;
        return (
          
    Title {/* */} {auth && (
    Profile My account
    )}
    ); } private getPathName(): string { if (!this.props.location) { return "/counter1"; } return (this.props.location as { pathname: string }).pathname; } private handleNavigate(event: React.ChangeEvent<{}>, value: any) { this.props.navigate(value as string); } private handleMenu(event: React.MouseEvent) { this.setState({ anchorEl: event.currentTarget }); } private handleClose() { this.setState({ anchorEl: undefined }); } } export default withStyles(styles)(ApplicationBar);

    Then you will tell me: "but where do I initiate the call that will fill my list?" Well I see here that you use redux-thunk (I prefer redux observable... more complicated to learn but waaaaaaaaaaaaaaaaay more powerful), then this should be thunk that initiates the dispatch of this!

    To summarize:

    • Components: the stupidest element that normally should have only the render method, and some other method handler to bubble up user events. This method only takes care of showing its properties to the user. Don't use the state unless you have a visual information that belongs only to this component (like the visibility of a popup for example). Anything that is showed or updated comes from above: a higher level component, or a container. It doesn't decide to update its own values. At best, it handles a user event on a subcomponent, then bubble up another event above, and... well maybe at some point, some new properties will be given back by its container!
    • Container: very stupid logic that consists in wrapping a top level component into redux for it to plug events to actions, and to plug some part of the store to properties
    • Redux thunk (or redux observable): it is the one that handles the whole user application logic. This guy is the only one who knows what to trigger and when. If a part of your front end must contain the complexity, it's this one!
    • Reducers: define how to organize the data in the store for it to be as easily usable as possible.
    • The store: ideally one per top level container, the only one that contains the data that must be showed to the user. Nobody else should.

    If you follow these principles, you should never face any issue like "why the hell this is called twice? and... who made it? and why at this moment?"

    Something else: if you use redux, use an immutability framework. Otherwise you may face issues as reducers must be pure functions. For this you can use a popular one immutable.js but not convenient at all. And the late ousider that is actually a killer: immer (made by the author or mobx).

提交回复
热议问题