Реагировать на API контекста, не передавая новое значение состояния всем потребителям. - PullRequest
0 голосов
/ 30 сентября 2018

Я создаю сайт, на котором есть довольно много реагирующих компонентов, но это не SPA или полностью реагирующий сайт.Поэтому реагирующие компоненты попадают в страницы.У меня есть компонент наложения, который покрывает пользовательский интерфейс, когда открываются различные меню или выпадающие.У меня проблема в том, что я настроил свой контекст следующим образом:

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
}

Затем я передаю этому провайдеру и потребителю 2 разных компонента:

  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
}

MyAccount:

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(е);из MyAccount.js он обновляет состояние в контексте, но затем не пересылает это изменение в Overlay.js, в котором стили «showOverlay» пропа основаны на этом логическом значении.Если я добавлю в свой аккаунт тестовую опору, он получит изменение.То же самое происходит, если я запускаю событие из Overlay.js.Реквизиты внутри оверлея обновляются с того состояния в контексте, но не в MyAccount.js

Я предполагаю, что я делаю что-то не так с несколькими поставщиками, возможно, но у меня сложилось впечатление, что несколько поставщиков были абсолютно в порядкеAPI реагирующего контекста.

Любое направление приветствуется!

1 Ответ

0 голосов
/ 30 сентября 2018

Контекст API опирается на иерархию компонентов.Изменения в одном Provider значении не будут волшебно доступны в другом Provider.На данный момент они совершенно не связаны.

Чтобы разделить значение между Consumer компонентами, должен быть один Provider, содержащий все Consumer.OverlayProvider является избыточным на этом этапе, потому что этот компонент не должен использоваться повторно.

Если это не SPA, то несвязанные компоненты должны отображаться как порталы под тем же Provider, как предложено в этот ответ .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...