Reactjs Context don't update - javascript

here's the code working:
https://codesandbox.io/s/bug-context-sptwvg?file=/src/templates/CardsTemplate.tsx
all my context work well, i can search for a card and the api give me the results, but i dont uderstand why the CardsTemplate dont update the list, when i try a console.log(cardsList) or in loading, it don't update, anyone now a solution?
import { Grid } from "#mui/material";
import { useCardsContext } from "hooks";
import { CardShow } from "components/Items";
import { makeStyles } from "#material-ui/styles";
const useStyles = makeStyles({
cards: {
display: "flex",
flexWrap: "wrap",
justifyContent: "center",
gap: 10,
padding: 5,
marginTop: 5,
},
});
function CardsTemplate() {
const classes = useStyles();
const { cardsList, loading } = useCardsContext();
return (
<Grid className={classes.cards}>
{loading ? (
<p>carregando...</p>
) : cardsList?.data?.length > 0 ? (
cardsList.data.map((card, index) => (
<Grid>
<CardShow key={index} card={card} />
</Grid>
))
) : (
<p>procure um card</p>
)}
</Grid>
);
}
export default CardsTemplate;

The problem is that you are using your CardProvider as a wrapper in multiple places/components, this is why your CardsTemplate is not accessing the correct data. To fix this you just need to wrap only your App top component (which is your app's entry point) with the Provider. This way your data will be accessible from anywhere in your app and you will have a single source of truth for this data, therefore, avoiding inconsistencies.

Related

Warning: Each child in a list should have a unique "key" prop. how to fix this?

Ive been using this project with out a problem and now all of a sudden I keep getting this error and it won't show my notes when I click on the my notes section. What do I have to do for it to go away. The backend is up and running and I can see the static data but it wont show on the app
import { makeStyles } from '#mui/styles'
import React from 'react'
import { Drawer } from '#mui/material'
import { Typography } from '#mui/material'
import List from '#mui/material/List'
import ListItem from '#mui/material/ListItem'
import ListItemIcon from '#mui/material/ListItemIcon'
import ListItemText from '#mui/material/ListItemText'
import { AddCircleOutlineOutlined, SubjectOutlined } from '#mui/icons-material'
import { useHistory, useLocation } from 'react-router-dom'
import AppBar from '#mui/material/AppBar'
import Toolbar from '#mui/material/Toolbar'
import { format } from 'date-fns'
import { red } from '#mui/material/colors'
const drawerWidth = 240 // 500 - subtract this number from
const useStyles = makeStyles((theme) => {
return{
page: {
background: '#E5E4E2',
width: '100%',
padding: theme.spacing(3)
},
drawer: {
width: drawerWidth
},
drawerPaper: {
width: drawerWidth
},
root: {
display: 'flex' //places the drawer side by side with the page content
},
active: {
background: '#E5E4E2'
},
// title:{
// padding: theme.spacing(13),
// alignItems: 'center'
// },
}})
export default function Layout({ children }) {
const classes = useStyles()
const history = useHistory()
const location = useLocation()
const menuItems = [
{
text: 'My Projects',
icon: <SubjectOutlined color="secondary" />,
path: '/'
},
{
text: 'Create Project',
icon: <AddCircleOutlineOutlined color="secondary" />,
path: '/create'
}
]
return (
<div className={classes.root}>
{/* side drawer */}
<Drawer
className={classes.drawer}
variant='permanent' //Lets MUI know we want it on the page permanently
anchor="left" // position of drawer
classes={{ paper: classes.drawerPaper}}
>
<div>
<Typography variant="h5" sx={{textAlign: 'center'}}>
Projects
</Typography>
</div>
{/* List / Links */}
<List>
{menuItems.map(item => (
<div className={location.pathname == item.path ? classes.active : null}>
<ListItem key={item.text} button onClick={() => history.push(item.path)}>
<ListItemIcon>{item.icon}</ListItemIcon>
<ListItemText primary={item.text} />
</ListItem>
</div>
))}
</List>
</Drawer>
<div className={classes.page}>
<div className={classes.toolbar}></div>
{children}
</div>
</div>
)
}
enter image description here
Updated
I'm sorry, of course, you should just move key to the parent div. I didn't notice it. Chris who answered in the comments is right and my answer was not needed. I rewrote the answer.
To have an unique key use index in map or like you did item.text if text is unique for each element in map.
menuItems.map((item,index) =>
The idea is that map has to contain unique key for each element.
In result we have:
<div key={item.text} className={location.pathname == item.path ? classes.active : null}>
or
<div key={index} className={location.pathname == item.path ? classes.active : null}>
And you need to remove key from the List.
Hope this helps! Regards,

I am trying to restart a GIF animation in a React using hooks

I have found similar questions with answers I have tried to modify I looked at this one in particular. The goal here is to get the GIF to restart every time this component is shown. My sample code has been through an iteration or two, and this is where it is as of now.
import {useState, useEffect} from "react"
import Footer from "../components/Footer";
import NavMenu from '../components/NavMenu'
import {Box, Typography} from '#mui/material'
import backgroundImage from '../assets/images/world-bg.png'
import Logo from '../assets/images/chr-logo-white#2x.png'
import landscape from '../assets/gifs/why-we-exist_1test.gif'
import { Gif } from "#mui/icons-material";
const Exist = () => {
const [gif, setGif] =useState('')
const reloadGif = () => {
setGif('')
setTimeout(() => {
setGif(landscape)
}, 0)
}
useEffect(() => {
reloadGif()
return () => {
setGif('')
}
},[]
)
const styles ={
introContainer:{
height: '100%',
width: '100vw',
backgroundImage: `url(${backgroundImage})`,
display: 'flex',
flexDirection: 'column',
alignContent: 'center',
color:'white',
textAlign:'center',
justifyContent: 'center',
alignContent:'center',
flexShrink:0
}
}
return (
<Box sx = {styles.introContainer}>
<NavMenu />
<Box >
{gif !== '' && <img src={gif} alt="Logo"></img>}
</Box>
<Footer logo={false} back={'intro'} next={"home"}/>
</Box>
)
}
export default Exist;
I was hoping that my useEffect would clear the image src and then set it again forcing a re-render. The problem I am seeing is that with the empty dependency array it won't trigger a re-render, but without it, I get constant re-renders, but with the GIF still only playing once.
The issue here is most likely that you don't have a clear way to retrigger the use effect to make the Gif reload.
There are several solutions to this, but it depends on how you implemented the logic to show and not show your component.
As you already realized a useEffect with an empty dependency array is triggered whenever the component is put on the screen.
As a solution you need to check out how you are currently rendering your component with the gif to the screen. You have to make sure it is newly added to the DOM, whenever you want to play the gif.
As a side note: There is no need for the return function in your useEffect.
const ParentComponent = () => {
const [showChildComponent, setShowChildComponent] = useState(true)
return (
<>
<button onClick={() => {setShowChildComponent(!showChildComponent)}}>
Click me to show or hide the child component
</button>
{ showChildComponent && <ChildComponent /> }
</>
)
}
const ChildComponent = () => {
const [gif, setGif] =useState('')
const reloadGif = () => {
setGif('')
setTimeout(() => {
setGif(landscape)
}, 0)
}
useEffect(() => {
reloadGif()
},[]
)
return (<img src={gif} alt="Logo"></img>)
}
You can also retrigger a useEffect with the help of a prop / state.
The following useEffect would be retriggered everytime your showGif state now changes, which you could set whenever you want to play your gif.
const [retriggerGif, setRetriggerGif] = useState(true)
useEffect(() => {
reloadGif()
},[retriggerGif]
)
In case you can not mount / unmount your component
In the comments you mentioned you use a hash based router and only wrap your component in a RouterLink.
In this case you could make your useEffect depend on the hash of your router and always rerun of the hash changes:
This of course depends on what router you are using but there is always a way to get the currently active path and hash somehow.
In react-router you e.g. can get the history object through the useHistory() hook like this:
const history = useHistory()
useEffect(() => {
// check that you are at the correct hash
if(history.hash === 'header') {
reloadGif()
}
}, [history])
Using #Carolin's answer as a starting point. I was able to come up with a solution, still feels hacky but it works.
import {useEffect} from "react"
import Footer from "../components/Footer";
import NavMenu from '../components/NavMenu'
import {Box} from '#mui/material'
import backgroundImage from '../assets/images/world-bg.png'
import landscape from '../assets/gifs/why-we-exist_1test.gif'
const Exist = ({viewCount, handleView}) => {
useEffect(() => {
return () => {
return handleView(viewCount + 1)
}
},[])
const styles ={
introContainer:{
height: '100%',
width: '100vw',
backgroundImage: `url(${backgroundImage})`,
display: 'flex',
flexDirection: 'column',
alignContent: 'center',
color:'white',
textAlign:'center',
justifyContent: 'center',
alignContent:'center',
flexShrink:0
}
}
return (
<Box sx = {styles.introContainer}>
<NavMenu />
<Box >
{viewCount %2 === 0 && <img src={landscape + "?=" + Date.now()} alt="Logo"></img>}
{viewCount %2 !== 0 && <img src={landscape + "?=" + Date.now()} alt="Logo"></img>}
</Box>
<Footer logo={false} back={'intro'} next={"home"}/>
</Box>
)
}
export default Exist;
where viewCount and handleView are const [count, setCount]=useState(0) props from parent component

Converting class to function React Error invalid hook call

I have been converting my class components to functions but I'm stuck on this hook error to do with my export default. I'm sure it's something simple, but I can't find the answer I'm looking for.
This is the code causing the error:
import React from 'react'
import {AppBar, Toolbar, Button, Typography, makeStyles} from '#material-ui/core'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
import Menu from './Menu'
const useStyles = makeStyles((theme) => ({
header: {
backgroundColor: "#1d3834",
},
root: {
flexGrow: 1,
},
menuButton: {
marginRight: theme.spacing(2),
},
title: {
flexGrow: 1,
},
}))
function Header(props) {
const classes = useStyles()
const renderContent = () => {
switch (props.auth) {
case null:
return
case false:
return (
<Button color="inherit" href="/signin">Sign In</Button>
)
default:
return (
<Button color="inherit" href="/api/logout">Sign Out</Button>
)
}
}
return(
<div className={classes.root}>
<AppBar position="static" className={classes.header}>
<Toolbar>
<Menu/>
<Typography variant="h6" className={classes.title} >
<Link
to={props.auth ? '/items' : '/'}
className="left brand-logo"
>
</Link>
</Typography>
{renderContent()}
</Toolbar>
</AppBar>
</div>
);
}
function mapStateToProps({auth}) {
return{ auth }
}
export default connect(mapStateToProps)(makeStyles(useStyles) (Header))
I'm hoping someone has ran into a similar issue before and would be able to give me some feedback, Thanks :)
The main issue is how you export your component. Try instead as the following:
export default connect(mapStateToProps)(Header)
You don't really need the makeStyles(useStyles) part there.
+1 suggestion - not related to the question:
This suggestion is not related to the question itself, it's just a small improvement how I like to organize makeStyles stuff in my code repository with Material-UI.
Usually I create a styles.tsx which would look like in your case as the following, placed next to your component file:
import { makeStyles } from "#material-ui/core"
const useStyles = makeStyles((theme) => ({
header: {
backgroundColor: "#1d3834",
},
root: {
flexGrow: 1,
},
menuButton: {
marginRight: theme.spacing(2),
},
title: {
flexGrow: 1,
},
}))
export default useStyles
Then import in the component as the following:
import useStyles from "./styles"
And the usage similarly as in your component:
function Header(props) {
const classes = useStyles()
// ... rest of your component
}

How can I conserve data when I change screen in React-Native? [duplicate]

This question already has answers here:
How to use global variables in React Native?
(7 answers)
Closed 2 years ago.
In my code, I have a page that shows a list of elements retrieved from a database. However, I also added a page that allows the user to select several filters.
I have two problems:
I must save the data after I get away from the page (it musts stay there)
I must transfer the data back to the list of elements.
I thought that by giving the filter screen a var from the previous screen, it would save the data in there, but it doesn't seem to work.
Do you have any indication about what I should do ?
import React from 'react'
import { SafeAreaView, View, Text } from 'react-native'
import {generalStyles} from '#gym-app/styles/general'
import { useTranslation } from 'react-i18next'
import { TouchableOpacity } from 'react-native-gesture-handler';
export default function ExerciseSelectorScreen({navigation}) {
const {t} = useTranslation();
var filterProps = {};
return (
<SafeAreaView style={generalStyles.contentContainer}>
<View style={[generalStyles.contentContainer]}>
<Text>{JSON.stringify(filterProps)}</Text>
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Text style={generalStyles.title}>{ t('exercise.exercises') }</Text>
<TouchableOpacity onPress={() => {navigation.navigate('Exercises Filter', {filterProps: filterProps})}} style={{padding: 10, backgroundColor: '#ffb623'}}>
<Text style={{fontSize: 20, fontWeight: 'bold'}}>{t('general.filters').toUpperCase()}</Text>
</TouchableOpacity>
</View>
</View>
</SafeAreaView>
)
}
import React, { useState, useEffect } from 'react'
import { View, SafeAreaView, Text, ScrollView, StyleSheet } from 'react-native'
import {generalStyles} from '#gym-app/styles/general'
import { useTranslation } from 'react-i18next'
import persistent from '#gym-app/database/persistent'
import tdb from '#gym-app/translation/object'
import CheckboxGroup from '../../components/workout/exercises/filters/CheckboxGroup'
import { useRoute } from '#react-navigation/native';
export default function ExercisesFilterScreen() {
const {t} = useTranslation();
const route = useRoute();
var filterProps = route.params.filterProps;
const [equipments, setEquipments] = useState({});
const [selectedEquipments, setSelectedEquipments] = useState({});
const [order, setOrder] = useState('');
const [machine, setMachine] = useState('yes');
useEffect(()=> {
if (Object.values(equipments).length == 0) {
persistent.transaction(tx => {
tx.executeSql(
"SELECT * FROM equipment",
[],
(t, s) => {
var transEquip = {};
Object.values(s.rows._array).map((equipment) => {
transEquip[equipment.id] = tdb(equipment, 'name')
})
setEquipments(transEquip);
},
(t, e) => {
console.log(e);
}
);
});
}
},[equipments])
useEffect(() => {
filterProps.selectedEquipments = selectedEquipments;
filterProps.order = order;
filterProps.machine = machine;
})
return (
<SafeAreaView style={{flex: 1}}>
<ScrollView style={[generalStyles.contentContainer, {flex: 1, backgroundColor: '#ffb623'}]}>
<Text style={{fontSize: 30, fontWeight: 'bold', color: '#ffffff', textAlign: 'center'}}>{t('general.filters').toUpperCase()}</Text>
<View style={{marginTop: 10}}>
<CheckboxGroup
title={t('exercise.availableEquipment')}
selected={selectedEquipments}
select={setSelectedEquipments}
multi={true}
options={
equipments
}
/>
<CheckboxGroup
title={t('exercise.order')}
selected={order}
select={setOrder}
multi={false}
options={
{
"asc": t('exercise.easyToHard'),
"desc": t('exercise.hardToEasy'),
}
}
undefined={'allow'}
/>
<CheckboxGroup
title={t('exercise.machines')}
selected={machine}
select={setMachine}
multi={false}
options={
{
"yes": t('exercise.machine.yes'),
"no": t('exercise.machine.no'),
"only": t('exercise.machine.only')
}
}
/>
</View>
</ScrollView>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
optionTitle: {
marginBottom: 6,
fontSize: 24,
fontWeight: 'bold',
color: '#ffffff',
textAlign: 'left'
}
})
Best way to use "global variable". By using this, you can access your data on multiple screen and re-render in your flat-list. (Redux is also the solution but initial is difficult to understand). you can initial you global variable in constructor and can update any where in the screen. here is link you can make understanding of global variable in react-native.
https://aboutreact.com/react-native-global-scope-variables/
Either you can use redux to store the state irrespective of if you navigate to naother screen or not. It will store the state globally and whenever you need you can use its mapStateToProps method to retrieve the state value. Check this doc : react-redux . You can also save the store data even when the app closes by using redux-persist, I personally prefer this as it lessens the hassle of using Asyncstorage.
Even you can use just Asyncstorage to save data.
Hope it helps .feel free for doubts

MUI Drawer set background color

How to simply set background color of MUI Drawer?
tried this, but doesn't work
<Drawer
style={listStyle3}
open={this.state.menuOpened}
docked={false}
onRequestChange={(menuOpened) => this.setState({menuOpened})}
/>
const listStyle3 = {
background: '#fafa00',
backgroundColor: 'red'
}
Edit: (May-21) - Material UI V4.11.1
This can be done differently in version 4.11.1 and functional components.
There's no need to use an HoC anymore. Here's how it's done:
You should use the makeStyles helper to create the hook with a definitions of the classes and use the hook to pull them out.
const useStyles = makeStyles({
list: {
width: 250
},
fullList: {
width: "auto"
},
paper: {
background: "blue"
}
});
const DrawerWrapper = () => {
const classes = useStyles();
return (
<Drawer
classes={{ paper: classes.paper }}
open={isOpen}
onClose={() => setIsOpen(false)}
>
<div
tabIndex={0}
role="button"
onClick={() => setIsOpen(true)}
classes={{ root: classes.root }}
>
{sideList}
</div>
</Drawer>
)
}
Here's a working sandbox
Edit: (Jan-19) - Material UI V3.8.3
As for the latest version asked, the way to configure the backgroundColor would be by overriding the classes.
Based on material-ui documentation here, and the css api for drawer here - This can be done by creating an object in the form of:
const styles = {
paper: {
background: "blue"
}
}
and passing it to the Drawer component:
<Drawer
classes={{ paper: classes.paper }}
open={this.state.left}
onClose={this.toggleDrawer("left", false)}
>
A working example can be seen in this codesandbox.
Don't forget to wrap your component with material-ui's withStyles HoC as mentioned here
Based on the props you used I have the reason to think that you're using a version which is lower than v1.3.1 (the last stable version) but for the next questions you'll ask, I recommend writing the version you're using.
For version lower than V1, you can change the containerStyle prop like this:
<Drawer open={true} containerStyle={{backgroundColor: 'black'}}/>
In MUI v5, you can use the sx prop to style MUI components:
<Drawer
PaperProps={{
sx: {
backgroundColor: "pink",
color: "red",
}
}}
Or use styleOverrides to define the custom styles in createTheme:
const theme = createTheme({
components: {
MuiDrawer: {
styleOverrides: {
paper: {
backgroundColor: "pink",
color: "red",
}
}
}
}
});
Material UI V4.3.2
As in this version you can change the backgroundColor by making use of makeStyles from '#material-ui/core/styles' as shown below:
import Drawer from '#material-ui/core/Drawer';
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles({
paper: {
background: 'black',
color: 'white'
}
});
const SideDrawer = props => {
const styles = useStyles();
return (
<Drawer
anchor="right"
open={props.drawerOpen}
onClose={() => props.toggleDrawer(false)}
classes={{ paper: styles.paper }}
>
Items
</Drawer>
);
};
export default SideDrawer;
Drawer doesn't accept style props. Use classes instead
See Drawer API
If anyone's looking for how to do this conditionally for dark/light mode, you can make 2 separate classes and use a conditional to use the right one in the component. Here's an example of how to modify #Yirenkyi's answer do achieve this:
import Drawer from '#material-ui/core/Drawer';
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles({
paperLight: {
background: 'white',
color: 'black'
},
paperDark: {
background: 'black',
color: 'white'
}
});
const SideDrawer = props => {
const userPrefersDarkMode = true; //here's your condition
const styles = useStyles();
return (
<Drawer
anchor="right"
open={props.drawerOpen}
onClose={() => props.toggleDrawer(false)}
classes={{ paper: userPrefersDarkMode ? styles.paperDark : styles.paperLight }}
>
Items
</Drawer>
);
};
export default SideDrawer;

Categories

Resources