I am making a wrapper for the settings page in order for it to be reusable on every settings page. In it, there is a Context Provider in which I pass the user data, but it never seems to pass anything although the query is successful.
SettingsWrap.tsx
export const UserContext = React.createContext(null);
const SettingsWrap = ({ children }) => {
const [session] = useSession();
const { data, loading } = useQuery<SettingsData, SettingsVars>(
SETTINGS_QUERY,
{
variables: {
id: session?.id,
},
skip: !session,
}
);
return (
<Grid container spacing={2}>
<Grid item xs={12} sm={4}>
<SettingList />
</Grid>
{data && !loading ? (
<UserContext.Provider value={data.userId}>
<Grid
item
xs={12}
sm={8}
style={{ height: "100vh", overflow: "auto" }}
>
<Container>{children}</Container>
</Grid>
</UserContext.Provider>
) : (
<Grid
item
xs={12}
sm={8}
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
}}
>
<CircularProgress />
</Grid>
)}
</Grid>
);
};
AccountSettings.tsx
const user: UserInterface = useContext(UserContext); // THIS IS NULL
What is wrong with this code?
A context has a default value and can then be set to different other values by the use of providers, much like you have done. The value of a provider affects only the components in the children subtree of the component. Conversely, the current value of a context is the value set by the closest ancestor provider or, if none exists, the default value of the context.
Check the following example:
export const NumberContext = React.createContext(0); // default value
export default function App() {
return (
<div>
<Consumer /> // #1, shows 0
<NumberContext.Provider value={1}>
<Consumer /> // #2, shows 1
<NumberContext.Provider value={2}>
<Consumer /> // #3, shows 2
</NumberContext.Provider>
<div>
<div>
<Consumer /> // #4, shows 1
</div>
</div>
</NumberContext.Provider>
</div>
);
}
export function Consumer() {
const number = useContext(NumberContext)
return <p>{number}</p>
}
Here is a sandbox with this example: https://codesandbox.io/s/react-contexts-6dlh1
The first Consumer (#1) has no ancestor provider so it shows the default value of the context created which is 0.
The second Consumer (#2) is the direct child of a provider with value 1, and so shows value 1.
The third Consumer (#3) has two ancestor providers but the closest ancestor provider provides the value 2, so that's the value this component shows.
The fourth and last Consumer (#4) is nested under the first provider (since it lies outside the second provider but still inside the children hierarchy of the first provider). Thus it has an ancestor provider with the value 1 so it will also show value 1.
In your example, SettingsList (parent of AccountSettings), which is the presumed consumer of UserContext, does not have an ancestor provider (since it does not lie under the provider you have included in the component tree). Thus running useContext(UserContext) will always return null, the default value or UserContext, in that subtree of your component. Try putting SettingsList in a subtree with an ancestor provider and it will work.
Apparently, when I used useContext inside the Subcomponent where I passed "user" to, it worked perfectly fine. It was my mistake to use the context in the parent instead of the actual component.
Related
Inside my react App, i have a component that shows a list of data fetched from my database.
Now i would like to refresh my component every "X" seconds so if the database values have changed, my component will show also those new values.
Here's my code...
const SingleBet = () => {
const { data } = useGetAllBetsQuery() //REDUX FETCH
return (
<Paper sx={{marginTop:"10px", background:"white"}}>
<Grid container xs={12} sx={{display:"flex", flexDirection:"row", height:"50px"}}>
//ITEMS NO NEEDED
</Grid>
{data.map((data, index) => {
return (
<Paper sx={sxPaperList} key={index}>
<Grid container rowSpacing={2} sx={sxGridContainer}>
<Grid item xs={4} >
{data.ImportoG1 > 0 ? (
<Typography variant="h6" sx={sxTypographyNames}>{data.Giocatore1}
<img src={star} alt="little_star" height="15"/></Typography>
) : (
<Typography variant="h6" sx={sxTypographyNames}>
{data.Giocatore1}
</Typography>
)}
//OTHER THINGS LIKE THIS
</Grid>
</Paper>
)
})}
</Paper>
)
}
How u can see i fetch data from my DATABASE using redux. Now i would like to refresh this component every 5 seconds so that if DATABASE changes, every 5 seconds my component refreshes and show new values
I tried playing with useEffect but i couldnt reach to get any good results. Please help me :D
Here's my useGetAllBetsQuery() code
export const liveApi = createApi({
reducerPath: "liveApi",
baseQuery: fetchBaseQuery({baseUrl: URL}),
endpoints: (builder) => ({
getAllBets: builder.query({
query: () => "live",
})
})
})
export const { useGetAllBetsQuery } = liveApi
I have a small question regarding passing functions between components that are not in parent/child relationship.
My structure inside App.
function App() {
return (
<div className="App">
<Header/>
<Pfl />
<Sdc/>
<Checkscan/>
</div>
);
}
Those 3 components have an on click function attached to a button i want the button from the pfl component to trigger all 3 on click functions.
When i click on the button in the pfl component i want to trigger the function running in the pfl component and the functions that are inside the Sdc,Checkscan component.
Whats the best way to do it and pass the functions from the other components so when i click the button inside the pfl component it will trigger all the methods from the other 2 components(Sdc,Checkscan)?
Or if I make a container that looks like this
export default function Apicontainer() {
return (
<React.Fragment>
<Pfl />
<Sdc />
<Checkscan />
<Button variant="contained">Start</Button>
</React.Fragment>
)
}
and in app.js i only have the Apicontainer.
How do i transfer all the functions to work in that button click Component
I just wrote some quick and dirty example code to show how you can share things between components via a parent component:
export default function Apicontainer() {
const [sharedState, setSharedState] = useState({sdc: null, checkScan: null})
function pflFunction() {
console.log('pflFunction')
// do your stuff here. I would update state with some reasonable data, and then pass
// the relevant data to the component that needs it. This is just an example.
setSharedState({sdc: 'sdcData', checkScan: 'checkScanData'})
}
return (
<React.Fragment>
<Pfl onClick={pflFunction} />
<Sdc data={sharedState.sdc}/>
<Checkscan data={sharedState.checkScan} />
<Button variant="contained">Start</Button>
</React.Fragment>
)
}
// Example of how to trigger a function inside a component (separate file):
export default function Sdc({data}){
const sdcFunction = useCallback(() => {
// implement the function you want to call here. useCallback makes sure to keep
// a stable reference to the function, so that you can rely on it in a useEffect
}, [])
useEffect(() => {
if(data){
// do something. This effect will run whenever the data or sdcFunction changes.
sdcFunction()
}
}, [data, sdcFunction])
return (
<div>your Sdc view code here</div>
)
}
For the record: If fplFunction is anything else than an onClick handler, you should make sure the function has a stable reference, using useCallback (as in the last component)
I'm using a 3rd party component named "Dialog" with the render method below. As you can see - the component has more than one className. I'd like to create a styled-component called StyledDialog which contains a prop that lets me override the width associated with the div that has the "SURFACE" className. Can this be done with Styled-Components - or do I need to bring the source code into my app and handle that manually.
render() {
const { className, children, onClose, open, ...otherProps } = this.props;
const ariaHiddenProp = open ? {} : { 'aria-hidden': true };
return (
<aside
className={classnames(
ROOT,
{
[ANIMATING]: this.state.animating,
[OPEN]: open,
},
className
)}
onClick={(e) => {
if (onClose) onClose(e);
}}
onTransitionEnd={() => {
this.setState({ animating: false });
}}
{...ariaHiddenProp}
>
<div
className={SURFACE}
onClick={(e) => {
e.stopPropagation();
}}
{...otherProps}
>
{children}
</div>
<div className={BACKDROP} />
</aside>
);
}
Based on your explanation, i think you should wrap this 3rd party component with styled method and apply your styles by referencing the corresponding classnames of that component from the wrapped styled component.
For instance, If the name of existing component is Hello, you can apply styling from a styled-component on any of its DOM children like this:
const StyledHello = styled(Hello)`
.${classes.SURFACE} {
width: 10rem;
border: 2px solid green;
}
`;
Working Demo
I'm using styled-components to style a parent and child element in a component:
function StyledDemo({
name,
light,
...props
}) {
return (
<Parent {...props}>
<Child>{name}</Child>
</Parent>
);
}
I have a light prop which is true/false - but I'm having an issue with styling the elements based on the value of that property:
const Parent = styled.div`
background-color: #000;
width: 100%;
${props => props.light && `
background-color: #ccc;
`}
`;
The styling only seems to work when I remove the prop being passed into the function individually.
Parent element uses correct styling based on light prop value when:
function StyledDemo({ name, ...props })
Parent element does NOT use correct styling based on light prop value when:
function StyledDemo({ name, light, ...props })
I can get it all working by setting the prop on the Parent and Child component, but this doesn't seem like it's the best way:
return (
<Parent {...props} light={light}>
<Child light={light}>{name}</Child>
</Parent>
);
Is this the correct way to apply styles to components based on props, or is there an issue with my approach?
I have a demo to tinker with if it helps:
https://www.webpackbin.com/bins/-Kfcsujw99cjU7ttqgTz
This is not related to styled-components but to the rest parameter.
When you do the rest operator, any property you "pick" out by name won't be contained in the ...rest variable. So when you do
const Button = ({ light, ...rest }) => ()
<Button light primary />
rest will only contain the primary property, but not light, that's now it's own variable.
If you did
const Button = ({ ...rest }) => ()
<Button light primary />
instead rest would also contain light.
So in your example, you're picking out light from ...props, so when you pass {...props} on to the parent it doesn't contain light anymore, so styled-components doesn't know it exists! Either you go with your first version, or you have to manually apply it to each component.
See MDN for more information about the rest parameter!
I have been tuning the VirtualScroll (List) component for almost whole day but no luck.
I'm building a web based chatting application in which uses the react-virtualized List to display the chatting messages. Since message may have different content and different height, I use react-measure to calculate the item height and issue the recomputeRowHeights in rowRenderer.
The result is bad, VirtuallScroll List will jump around whenever I stopped the scrolling. For example, when I scrolled to the half of browser, I should see the middle of the messages, but it always suddenly shift the offset. Please take a look at the recorded video:
https://drive.google.com/file/d/0B_W64UoqloIkcm9oQ08xS09Zc1k/view?usp=sharing
Since I only use the List and Autosizer component, I only adapt the required css file into my project which is like
```
.VirtualScroll {
width: 100%;
outline: none;
}
```
For the render method, I nested a lot of flex components inside the rowRender:
Here is the code:
```
render() {
const inChat = this.context.store.getState().inChat;
const {conversationList} = this.state;
const imgUrl = 'img/builtin-wallpaper-1.jpg';
const backgroundStyle = {
backgroundImage: 'url(' + imgUrl + ')',
backgroundRepeat: 'no-repeat',
backgroundSize: 'cover',
backgroundPosition: 'top left'
};
if (inChat.id === this.id && inChat.status === 'FETCHING'){
return (
<Box column center height="80%">
<CircularProgress />
</Box>
);
} else if (inChat.id === this.id && inChat.status === 'FETCHED'){
return (
<Box column flex="1 0 100%" style={backgroundStyle}>
<HorizontalToolBar/>
<AutoSizer disableHeight={true}>
{({ width }) => (
<List
ref={(element) => {this.VirtualScroll = element;}}
className='VirtualScroll'
height={window.innerHeight - toolbarHeight - textAreaHeight}
overscanRowCount={10}
noRowsRenderer={this._noRowsRenderer.bind(this)}
rowCount={conversationList.length}
rowHeight={i => {
return (Measured_Heights[i.index] | 20); // default Height = 58
}}
rowRenderer={this._rowRenderer}
scrollToIndex={undefined} // scroll to latest item
width={width}
/>
)}
</AutoSizer>
<InputControl chatId={this.id} sendChatText={this._sendChatText.bind(this)}/>
</Box>
);
} else {
return null;
}
}
_rowRenderer ({ index, key, style, isScrolling }) {
console.log(Measured_Heights);
const rowData = this._getDatum(index);
// let renderItem;
// console.log('index = ' + index + ' key = ' + key);
if (rowData.type == 'conversation') {
if (rowData.data.type == netModule.TYPE_SYSTEM) {
// system message
return (
<Measure key={key} onMeasure={(dims) => this._onMeasure(index, dims)}>
<SystemMessage data={rowData.data}/>
</Measure>
)
}
if (rowData.data.senderId == this.state.selfProfile.username) {
// outgoing message
return (
<Measure key={key} onMeasure={(dims) => this._onMeasure(index, dims)}>
<RightMessage
screenWidth={(window.innerWidth - leftToolBarWidth) / 2 }
screenHeight={window.innerHeight - toolbarHeight}
data={rowData.data}/>
</Measure>
);
} else {
// incoming message
// append userProfile to left messages
return (
<Measure key={key} onMeasure={(dims) => this._onMeasure(index, dims)}>
<LeftMessage
userId={rowData.data.senderId}
userProfile={this.state.groupUserProfiles[rowData.data.senderId]}
screenWidth={(window.innerWidth - leftToolBarWidth) / 2 }
screenHeight={window.innerHeight - toolbarHeight}
data={rowData.data}/>
</Measure>
);
}
}
}
```
I read a couple docs that Flexbox may be intercept the scrolling event, but even though I added overflow-y: hidden to nested component I didn't see the issue disappear. Have you ever seen this wrong scrolling behavior with List component before?
Any suggestion is welcome.
I can't see the video, but I think I had something similar recently. From what I can see, you're not making use of the style parameter passed into your _rowRenderer method. This parameter contains some CSS transforms that make the row appear at the right vertical position in the scrolling list.