代码之家  ›  专栏  ›  技术社区  ›  WebDeg Brian

道具更改时由map()引起的意外渲染

  •  0
  • WebDeg Brian  · 技术社区  · 6 年前

    我和React和Redux一起工作。我希望Redux存储客户端信息(视口尺寸、浏览器名称和版本、操作系统等)。我已经设置了减速器和操作,并将所有状态映射到 <App /> 组件的道具。内部 <应用程序/> 组件有一个 <Header /> <NavigationBar /> 组件。 <导航栏/> 然后映射要渲染的道具 <NavigationItems /> 然后生成以下导航栏:

    Home  | About us | Apply | News | Login
    

    但是由于里面的道具映射 <应用程序/> 组件,每次用户调整视口大小时,Redux存储区内的状态都会发生更改,从而导致内部出现道具 <应用程序/>

    Home | Home | About us | Apply | News | Login
    

    或者

    About us | Home | About us | Apply | News | Login
    

    <导航栏/> 为了更好地说明这一点,我加入了代码结构:

    Store (contains client info) 
    ---> <App /> (map states, dispatchers from store to props) 
    ---> <Header /> (functional component acts as container, pass navigation settings as props down to <NavigationBar />)
    ---> <NavigationBar /> (receives props from <Header /> and renders <NavigationItems /> component with props from <Header /> using map() function)
    ---> <NavigationItems /> (renders navigation items based on the props)
    

    export const tUpdateViewport = 'updateViewport'
    

    根操作.js

    import { tUpdateViewport } from './rootTypes';
    
    export const aUpdateViewport = () => ({type: tUpdateViewport })
    

    根还原剂.js (定义app reducer并将其与其他应用程序合并)

    import { tUpdateViewport } from './rootTypes';
    import { combineReducers } from 'redux';
    
    const appStates = {
        viewportWidth: document.documentElement.clientWidth,
        viewportHeight: document.documentElement.clientHeight,
    }; 
    
    const appReducer = (states = appStates, action) => {
        switch(action.type) {
            case tUpdateViewport: 
                return {
                    ...states,
                    viewportWidth: document.documentElement.clientWidth,
                    viewportHeight: document.documentElement.clientHeight,
                };
            default:
                return states;
        }
    };
    
    export default combineReducers({
        app: appReducer,
    })
    

    应用程序.js < 组件和渲染 < )

    import React from 'react';
    import Header from './components/container/header/Header';
    import * as actions from './rootActions';
    import { connect } from 'react-redux';
    
    const mapStatesToProps = states => ({
        browserInfo: states.app
    });
    
    class App extends React.Component {
        componentDidMount = () => {
            window.addEventListener('resize', this.props.aUpdateViewport, false);
        };
        componentWillUnmount = () => {
            window.removeEventListener('resize', this.props.aUpdateViewport, false);
        };
      render = () => {
        return (
                <Header />
        );
      };
    }
    
    export default connect(mapStatesToProps, actions)(App)
    

    标题.js (定义 <标题/> 组件和渲染 <导航栏/>

    import React from 'react';
    import NavigationBar from '../../presentational/header/NavigationBar';
    import { Container, Grid, Row, Col } from '../../../css/Grid.css';
    import { Header as Navigator } from '../../../css/Layout.css';
    import logo from '../../../media/images/logo.png';
    
    const Header = props => {
        return (
            <Navigator>
                <Container>
                    <header>
                        <Grid>
                            <Row>
                                <Col>
                                    <a href="#"><img src={logo} alt="CKY-UK Logo" /></a>
                                </Col>
                                <Col stretchWidth>
                                    <Grid>
                                        <Row stretchHeight alignEnd="md" alignMiddle="md">
                                            <NavigationBar settings={{
                                                items:
                                                [
                                                    {
                                                        title: 'Home',
                                                        link: '/'
                                                    },
                                                    {
                                                        title: 'About us',
                                                        dropdownHierarchy: 'parents',
                                                        icons: [{
                                                            title: 'chevron-down',
                                                            iconPos: 'afterText'
                                                        }],
                                                        items: [{
                                                            title: 'Leadership Team',
                                                            link: '#leadership-team'
                                                        }, 
                                                        {
                                                            title: 'Star players',
                                                            link: '#star-players'
                                                        }]
                                                    },
                                                    {
                                                        title: 'Apply',
                                                        link: '#apply'
                                                    },
                                                    {
                                                        title: 'News',
                                                        link: '#news'
                                                    },
                                                    { 
                                                        title: 'Login',
                                                        link: '#login',
                                                        icons: [{
                                                            title: 'sign-in-alt',
                                                            iconPos: 'beforeText'
                                                        }],
                                                    }
                                                ]
                                                }} />
                                        </Row>
                                    </Grid>
                                </Col>
                            </Row>
                        </Grid>
                    </header>
                </Container>
            </Navigator>
        );
    };
    
    export default Header
    

    导航栏.js (定义 <导航栏/> 要渲染的组件和贴图道具 <导航项目/> 组件)

    import React from 'react';
    import NavigationItems from './NavigationItems';
    import { uniqID } from '../../../common/Functions';
    import { Row } from '../../../css/Grid.css';
    
    const NavigationBar = props => {
        const settings = props.settings;
        return (
            <nav>
                <ul>
                    <Row>
                        {settings.items.map((contents, index) => 
                            <NavigationItems settings={{...contents}} isDropdown={contents.items ? true : false} key={uniqID()} />
                        )}
                    </Row>
                </ul>
            </nav>
        );
    };
    
    export default NavigationBar
    

    (定义 < 并渲染导航栏)

    import React from 'react';
    import { uniqID } from '../../../common/Functions';
    import { NavLi, NavItem, Dropdown } from '../../../css/Navigation.css';
    import { Icon } from '../../../css/Common.css';
    import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
    
    class NavigationItems extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
                dropdownOpened: false,
                navLiWidth: null,
                navLiHeight: null
            }
        }
        handleClick = e => {
            e.preventDefault();
            this.setState(prevState => ({
                dropdownOpened: !prevState.dropdownOpened
            }));
        };
        handleClickOutside = e => {
            const target = e.target;
            if (!this.NavLi.contains(target)) {
                this.setState({
                    dropdownOpened: false
                })
            }
        }
        componentDidMount = () => {
            const navLiWidth = this.NavLi.clientWidth,
                        navLiHeight = this.NavLi.clientHeight;
            this.setState({
                navLiWidth: navLiWidth,
                navLiHeight: navLiHeight
            });
            document.addEventListener('click', this.handleClickOutside, false);
        }
        componentWillUnmount = () => {
            document.removeEventListener('click', this.handleClickOutside, false);
        }
        render = () => {
            const settings = this.props.settings,
                        isDropdown = this.props.isDropdown
            return (
                <NavLi isDropdown={isDropdown} innerRef={x => this.NavLi = x}>
                    <NavItem href={settings.link || '#'} onClick={isDropdown ? this.handleClick : null}>
                    {settings.icons && settings.icons.filter(contents => contents.iconPos === 'beforeText').map(contents => 
                        <Icon beforeText key={uniqID()}><FontAwesomeIcon icon={contents.title} fixedWidth /></Icon> 
                    )} 
                    {settings.title}
                    {settings.icons && settings.icons.filter(contents => contents.iconPos === 'afterText').map(contents => 
                        <Icon afterText key={uniqID()}><FontAwesomeIcon icon={contents.title} fixedWidth /></Icon>
                    )}
                    </NavItem>
                    {isDropdown && 
                        <Dropdown hierarchy={settings.dropdownHierarchy} opened={this.state.dropdownOpened} topPos={this.state.navLiHeight} leftPos={this.state.navLiWidth} desPos={10} startPos={50}>
                            {settings.items.map(contents => 
                                <NavigationItems settings={{...contents}} isDropdown={contents.items ? true : false} key={uniqID()} />
                                                                                                                                                                                                                                                                                                                                        )} 
                                                                                                                                                                                                                                                                                                             </Dropdown>
                  }
            </NavLi> 
            );
        };
    };
    
    export default NavigationItems
    

    我怎样才能解决这个问题?曾在谷歌上搜索过地图问题,但没有找到想要的结果。任何帮助都将不胜感激。

    1 回复  |  直到 6 年前
        1
  •  1
  •   SamVK    6 年前

    我猜是的 uniqID

    这个 key prop 特别存在于 title