我正在建立一个站点,其中包含很多反应组件,但不是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
}
然后,我将此提供者和消费者分为两个不同的部分:
这里是: 重叠:
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绝对可以。
任何方向都赞赏!
答案 0 :(得分:1)
上下文API依赖于组件层次结构。一个Provider
值的更改将不会神奇地在另一个Provider
中可用。在这一点上,它们是完全无关的。
为了在Consumer
个组件之间共享一个值,应该有一个包含所有Provider
的单个Consumer
。 OverlayProvider
在这一点上是多余的,因为不应重复使用此组件。
如果这是非SPA,则应按照this answer中的建议,将不相关的组件呈现为同一Provider
下的门户。