React上下文API不会将新的状态值传递给所有Consumer道具

时间:2018-09-30 13:19:14

标签: reactjs react-context

我正在建立一个站点,其中包含很多反应组件,但不是SPA或完整的反应站点。因此,反应组件被拉入页面。当出现各种菜单的打开或下拉菜单时,我有一个覆盖Ui的覆盖组件。我遇到的问题是我已经像这样设置了上下文:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

export const OverlayContext = React.createContext();

export class OverlayProvider extends Component {
    state = {
            showOverlay: true,
            test: 'default test value'
    }

    showHideOverlay = () => {
        const visible = this.state.showOverlay;
        this.setState({
            showOverlay: visible ? false : true
        });
    }

    render() {
        return (
            <OverlayContext.Provider value={{
                state: this.state,
                showHideOverlay: this.showHideOverlay
            }}>
                {this.props.children}
            </OverlayContext.Provider>
        );
    }
}

OverlayProvider.propTypes = {
    children: PropTypes.object
}

然后,我将此提供者和消费者分为两个不同的部分:

  1. 我的叠加层组件
  2. 我的MyAccount组件。

这里是: 重叠:

import React from 'react';
import { render } from 'react-dom';
import PropTypes from 'prop-types'

import { OverlayProvider, OverlayContext } from './../contexts/OverlayContext';


// Styling
import styled from 'styled-components'
import { colors } from '../global-styled-constants/colors';
import { mixins } from '../global-styled-constants/mixins';
import { sizing } from '../global-styled-constants/sizing';

//Animations
import { fadeIn } from '../global-styled-constants/animations';
import { fadeOut } from '../global-styled-constants/animations';

const PageOverlay = styled.div`
    ${mixins.dimensions('100%', '100%')};
    ${mixins.position('fixed', '0', '0')};
    display: block;
    background: ${colors.red};
    z-index: ${sizing.zIndex2};
    ${props => props.showOverlay ? `animation: ${fadeIn} 1s 0.3s both;` : `animation: ${fadeOut} 1.3s both;`}
`;

// the state containing the count will be fetched via the API and delivered to this component via redux
export const Overlay = () => {
    return (
        <OverlayProvider>
            <OverlayContext.Consumer>
                {(value) => (
                    <PageOverlay showOverlay={value.state.showOverlay}/>
                )}
            </OverlayContext.Consumer>
        </OverlayProvider>
    );
}

render(<Overlay/>, document.getElementById('react-overlay'))


Overlay.propTypes = {
    showOverlay: PropTypes.bool.isRequired
}

我的帐户:

import React, { Component, Fragment } from 'react';
import { OverlayProvider, OverlayContext } from '../../contexts/OverlayContext';

// Styling
import styled from 'styled-components'
import { colors } from '../../global-styled-constants/colors'
import { mixins } from '../../global-styled-constants/mixins'
import { sizing } from '../../global-styled-constants/sizing'
import { type } from '../../global-styled-constants/typography'
// import { mq } from '../../global-styled-constants/mediaQueries';

const Wrapper = styled.div`
    position: relative;
`;

const List = styled.ul`
    ${mixins.dimensions('195px', 'auto')};
    position: absolute;
    background: ${colors.lightSmoke};
    z-index: 10;
    top: 38px;
    padding: 15px;
    ${props => props.showDropdown ? `display: block;` : `display: none`};

`;

const Item = styled.li`
    padding: 15px 0;

    a {
        font-size: ${sizing.scaleh10};
        font-family: ${type.fsSanSerif};
        font-weight: ${sizing.fontWeightBold};
        font-style: normal;
        text-decoration: none;

    }

    &:hover {
        cursor: pointer;
        color: ${colors.orange};
    }
`;

const Trigger = styled.li`
    ${mixins.flex('center')};
    height: 38px;
    font-size: ${sizing.scaleh10};
    padding: 0 20px;
    position: relative;
    color: ${colors.anotherGrey};
    font-family: ${type.fsSanSerif};
    font-weight: ${sizing.fontWeightBold};
    font-style: normal;
    text-decoration: none;
    cursor: pointer;
    ${props => props.showDropdown && `background: ${colors.lightSmoke}; color: ${colors.orange}`};
`;


const accountData = {
    "loggedOut": {
        "displayCTA": "My Account",
        "links": [
            {
                "displayName": "Sign In",
                "link": "www.google.com"
            },
            {
                "displayName": "Sign up / Join",
                "link": "www.google.com"
            },
            {
                "displayName": "Register Products",
                "link": "www.google.com"
            },
            {
                "displayName": "Subscribe to Newsletter",
                "link": "www.google.com"
            }
        ]
    },
    "loggedIn": {
        "displayCTA": "{userEmail}",
        "links": [
            {
                "displayName": "My Products",
                "link": "www.google.com"
            },
            {
                "displayName": "My Wishlist",
                "link": "www.google.com"
            },
            {
                "displayName": "My Orders",
                "link": "www.google.com"
            },
            {
                "displayName": "My Profile",
                "link": "www.google.com"
            },
            {
                "displayName": "Sign Out",
                "link": "www.google.com"
            },
            {
                "displayName": "Register Products",
                "link": "www.google.com"
            },
            {
                "displayName": "Newsletter Sign up",
                "link": "www.google.com"
            }
        ]
    }
}




export default class MyAccount extends Component {
    constructor(props) {
        super(props);
        this.state = {
            showDropdown: false,
            loggedIn: false
        }
    }

    hoverAwakeMenu = () => {
        this.setState({
            showDropdown: true
        })
    }

    mouseLeft = () => {
        this.setState({
            showDropdown: false
        });
    }

    render() {
        const { showDropdown, loggedIn } = this.state;
        const data = loggedIn ? accountData.loggedIn.links : accountData.loggedOut.links;
        const displayCTA = loggedIn ? accountData.loggedIn.displayCTA : accountData.loggedOut.displayCTA;

        const itemsList = data.map((item, i) => <Item key={i}><a href={item.link}>{item.displayName}</a></Item>);

        return (
            <OverlayProvider>
                <Fragment>
                    <Wrapper onMouseLeave={this.mouseLeft}>
                        <OverlayContext.Consumer>
                            {(value) => (
                                <Trigger showDropdown={showDropdown} onMouseEnter={(e) => {this.hoverAwakeMenu(e); value.showHideOverlay(e);}}>{displayCTA}</Trigger>
                            )}
                        </OverlayContext.Consumer>
                        <List showDropdown={showDropdown}>
                            {itemsList}
                        </List>
                    </Wrapper>
                </Fragment>
            </OverlayProvider>
        );
    }
}

当我调用value.showHideOverlay(e);从MyAccount.js会更新上下文中的状态,但不会将此更改转发到Overlay.js,在此处Pro'showOverlay'会基于此布尔值进行样式设置。如果我在自己的帐户中放入了测试道具,它将得到零钱。如果我从Overlay.js触发事件,也会发生同样的事情。叠加层中的道具会从那时开始更新上下文状态,但不会更新MyAccount.js中的道具

我想也许我对多个提供程序做错了事,但我的印象是多个提供程序使用react context API绝对可以。

任何方向都赞赏!

1 个答案:

答案 0 :(得分:1)

上下文API依赖于组件层次结构。一个Provider值的更改将不会神奇地在另一个Provider中可用。在这一点上,它们是完全无关的。

为了在Consumer个组件之间共享一个值,应该有一个包含所有Provider的单个ConsumerOverlayProvider在这一点上是多余的,因为不应重复使用此组件。

如果这是非SPA,则应按照this answer中的建议,将不相关的组件呈现为同一Provider下的门户。