Related
In a media player application, I try to use "expo-av" library to build a playlist. everything is working fine. But when I press on the backbutton, it is not behaving properly. I tried in many way. but nothing works for me.
I tried while handling backButton, like, sound.unloadAsync(), sound.stopAsync(), setSound(null).
import React, { useEffect, useState } from 'react';
import {
View,
BackHandler,
Text,
TouchableWithoutFeedback,
StyleSheet,
} from 'react-native';
import * as Progress from 'react-native-progress';
import { connect } from 'react-redux';
import { MaterialCommunityIcons } from '#expo/vector-icons';
import { Audio } from 'expo-av';
const sectionsAllCards = [
{
id: 'audio-01',
name: 'Body scan: Generic under mindfulness',
link: 'Bodyscan.m4a',
}
];
const MusicPlayerList = ({ navigation, route, ...props }) => {
const [isPlaying, setIsPlaying] = useState(false);
const [progress, setProgress] = useState(0);
const [audioIndex, setAudioIndex] = useState(0);
const [soundObject, setSoundObject] = useState(null);
const audioSources = [
require('../../assests/musics/Bodyscan.m4a')
];
const togglePlayback = async () => {
if (isPlaying) await soundObject.pauseAsync();
else await soundObject.playAsync();
setIsPlaying(!isPlaying);
};
const onPlaybackStatusUpdate = (status) => {
setProgress(status.positionMillis / status.durationMillis);
};
useEffect(() => {
const loadAudio = async () => {
const source = audioSources[audioIndex];
const sound = new Audio.Sound();
try {
await sound.loadAsync(source);
setSoundObject(sound);
sound.setOnPlaybackStatusUpdate(onPlaybackStatusUpdate);
} catch (error) {
console.log(error);
}
};
loadAudio();
}, [audioIndex]);
async function handleBackButtonClick() {
navigation.navigate('LoginSignup');
return true;
}
useEffect(() => {
BackHandler.addEventListener(
'hardwareBackPress',
handleBackButtonClick,
);
return () => {
BackHandler.removeEventListener(
'hardwareBackPress',
handleBackButtonClick,
);
};
}, []);
const handleOnPress = async (index) => {
if (index === audioIndex) togglePlayback();
else {
setIsPlaying(false);
setProgress(0);
await soundObject.stopAsync();
setSoundObject(null);
setAudioIndex(index);
}
};
return (
<View style={{ backgroundColor: '#efefef', flex: 1 }}>
{sectionsAllCards.map((card, index) => (
<TouchableWithoutFeedback
key={card.id}
onPress={() => handleOnPress(index)}
>
<View style={styles.boxContainer}>
<Text style={styles.audioText}>{card.name}</Text>
<View style={styles.audioIconContainer}>
{progress >= 0 && progress <= 1 && (
<View>
<Progress.Circle
style={styles.progress}
progress={audioIndex === index ? progress : 0}
indeterminate={false}
showsText={false}
size={60}
borderWidth={2}
color={'#479162'}
/>
<Text
style={{
position: 'absolute',
left: 11,
top: 10,
}}
>
<MaterialCommunityIcons
name={
isPlaying && audioIndex === index
? 'pause'
: 'play'
}
size={38}
style={{ color: '#479162' }}
/>
</Text>
</View>
)}
</View>
</View>
</TouchableWithoutFeedback>
))}
</View>
);
};
const styles = StyleSheet.create({
boxContainer: {
},
audioText: {
},
});
const mapStateToProps = (state) => ({
accessToken: state.auth.accessToken,
});
export default connect(mapStateToProps, {})(MusicPlayerList);
//StudentsFilter.jsx
import {
Accordion,
AccordionButton,
AccordionIcon,
AccordionItem,
AccordionPanel,
Badge,
Box,
Button,
Checkbox,
Flex,
Radio,
RadioGroup,
Text,
useColorMode,
useColorModeValue,
VStack,
} from "#chakra-ui/react";
import React, { useState } from "react";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { axiosInstance } from "../../../axiosConfig";
import { commonSlidercss, darkSlidercss } from "../../../GlobalStyles";
import {
setFilterSearchMode,
setSearchingBatch,
setSearchingStream,
} from "../../../redux/slices/adminUserSlice";
const StudentsFilter = () => {
const [streams, setStreams] = useState(null);
const [batchYear, setBatchYear] = useState([]);
const [checkedStreams, setCheckedStreams] = useState([]);
const [checkedBatches, setCheckedBatches] = useState([]);
const siteState = useSelector((state) => state.siteReducer);
const adminUsers = useSelector((state) => state.adminUserReducer);
const filterSearchMode = adminUsers?.filterSearchMode;
const site = siteState.siteInfo;
const { colorMode } = useColorMode();
const dispatch = useDispatch();
useEffect(() => {
getStream();
let batches = [];
if (site) {
let year = site.year_established;
let current_year = new Date().getFullYear();
let year_diff = current_year - site.year_established;
for (let i = 0; i <= year_diff; i++) {
batches.push(year + i);
}
setBatchYear(batches);
}
}, [site]);
const getStream = async () => {
try {
const res = await axiosInstance.get("stream");
setStreams(res?.data?.stream);
} catch (error) {
console.log("Something went wrong while getting streams", e);
}
};
const streamsHandler = (e, li) => {
e.stopPropagation();
const index = checkedStreams.indexOf(li);
if (index > -1) {
setCheckedStreams([
...checkedStreams.slice(0, index),
...checkedStreams.slice(index + 1),
]);
} else {
setCheckedStreams([...checkedStreams, li]);
}
};
const batchesHandler = (e, li) => {
e.stopPropagation();
const index = checkedBatches.indexOf(li);
if (index > -1) {
setCheckedBatches([
...checkedBatches.slice(0, index),
...checkedBatches.slice(index + 1),
]);
} else {
setCheckedBatches([...checkedBatches, li]);
}
};
useEffect(() => {
dispatch(setSearchingStream(checkedStreams));
dispatch(setSearchingBatch(checkedBatches));
}, [checkedBatches, checkedStreams]);
return (
<Flex
p="6"
direction="column"
style={{ height: "inherit" }}
align="space-between"
justify="space-between"
w="300px"
maxH={231}
overflowY="scroll"
css={colorMode === "light" ? commonSlidercss : darkSlidercss}
>
<Box>
<Text fontWeight="medium" fontSize="sm" mb={7}>
More filters
</Text>
<Accordion allowMultiple>
<AccordionItem>
<AccordionButton>
<Box flex="1" fontSize="xs" textAlign="left">
Batch
</Box>
<AccordionIcon />
</AccordionButton>
<AccordionPanel pb={4}>
<RadioGroup>
<VStack align="start">
{batchYear &&
batchYear.map((li) => (
<Checkbox
// onChange={checkboxChange}
key={li}
value={li}
colorScheme={useColorModeValue(
"primaryScheme",
"purple"
)}
size="sm"
onChange={(e) => batchesHandler(e, li)}
isChecked={checkedBatches.includes(li)}
>
<Text fontSize="xs">{li}</Text>
</Checkbox>
))}
</VStack>
</RadioGroup>
</AccordionPanel>
</AccordionItem>
<AccordionItem>
<AccordionButton>
<Box flex="1" textAlign="left" fontSize="xs">
Stream
</Box>
<AccordionIcon />
</AccordionButton>
<AccordionPanel pb={4}>
<RadioGroup>
<VStack align="start">
{streams &&
streams.map((li) => (
<Checkbox
// onChange={checkboxChange}
key={li.id}
value={li.id}
colorScheme={useColorModeValue(
"primaryScheme",
"purple"
)}
size="sm"
onChange={(e) => streamsHandler(e, li.id)}
isChecked={checkedStreams.includes(li.id)}
>
<Text fontSize="xs">{li?.name}</Text>
</Checkbox>
))}
</VStack>
</RadioGroup>
</AccordionPanel>
</AccordionItem>
</Accordion>
</Box>
<Box>
<Button
width="full"
h="40px"
borderRadius="10px"
fontWeight="500"
variant="primary"
mt="10px"
onClick={() => dispatch(setFilterSearchMode(!filterSearchMode))}
>
Filter
</Button>
</Box>
</Flex>
);
};
export default StudentsFilter;
What is the reason why I am getting the error " React has detected a change in the order of Hooks called by StudentsFilter. This will lead to bugs and errors if not fixed" I have seen this warning in 2-3 components and also tried to correct it but I don't know what I am doing wrong? Can someone help me to identify it?
You're calling the useColorModeValue conditionally (and in a loop) in the return statement. That's probably the source of the error.
You should use ESLint and the "rules of hooks" rule, it would have been highlighted directly in your editor.
I have created a common table component in which I am calling in the file that are needed under some table there is an field action which have an edit and delete icon whenever I click on edit icon it should trigger a dropdown that will allow to update the status of component. But when I click on edit icon the table component will refresh so the menu is not opening at suitable position. Please help me
TableComponent
import React, { useState } from 'react';
import { Table, TableHead, TableRow, TableCell, makeStyles, TablePagination, TableSortLabel } from '#material-ui/core';
import PropTypes from 'prop-types';
const useStyles = makeStyles(theme => ({
table: {
marginTop: theme.spacing(3),
'& thead th': {
fontWeight: '600',
color:'white',
backgroundColor: '#143174',
},
'& tbody td': {
fontWeight: '300',
},
},
}))
export default function TableComponent(records, headCells,filterFn) {
const classes = useStyles();
const pages = [10, 50, 100]
const [page, setPage] = useState(0)
const [rowsPerPage, setRowsPerPage] = useState(pages[page])
const [order, setOrder] = useState()
const [orderBy, setOrderBy] = useState()
const TblContainer = props => (
<Table className={classes.table}>
{props.children}
</Table>
)
TblContainer.propTypes = {
children:PropTypes.element
}
const TblHead = props => {
const handleSortRequest = cellId => {
const isAsc = orderBy === cellId && order === "asc";
setOrder(isAsc ? 'desc' : 'asc');
setOrderBy(cellId)
}
return (<TableHead>
<TableRow>
{
headCells.map(headCell => (
<TableCell key={headCell.id}
sortDirection={orderBy === headCell.id ? order : false}>
{headCell.disableSorting ? headCell.label :
<TableSortLabel
active={orderBy === headCell.id}
direction={orderBy === headCell.id ? order : 'asc'}
onClick={() => { handleSortRequest(headCell.id) }}>
{headCell.label}
</TableSortLabel>
}
</TableCell>))
}
</TableRow>
</TableHead>)
}
const handleChangePage = (event, newPage) => {
setPage(newPage);
}
const handleChangeRowsPerPage = event => {
setRowsPerPage(parseInt(event.target.value, 10))
setPage(0);
}
const TblPagination = () => (<TablePagination
component="div"
page={page}
rowsPerPageOptions={pages}
rowsPerPage={rowsPerPage}
count={records.length}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
/>)
function stableSort(array, comparator) {
const stabilizedThis = array.map((el, index) => [el, index]);
stabilizedThis.sort((a, b) => {
const order = comparator(a[0], b[0]);
if (order !== 0) return order;
return a[1] - b[1];
});
return stabilizedThis.map((el) => el[0]);
}
function getComparator(order, orderBy) {
return order === 'desc'
? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy);
}
function descendingComparator(a, b, orderBy) {
if (b[orderBy] < a[orderBy]) {
return -1;
}
if (b[orderBy] > a[orderBy]) {
return 1;
}
return 0;
}
const recordsAfterPagingAndSorting = () => {
return stableSort(filterFn.fn(records), getComparator(order, orderBy))
.slice(page * rowsPerPage, (page + 1) * rowsPerPage)
}
return {
TblContainer,
TblHead,
TblPagination,
recordsAfterPagingAndSorting
}
}
ParentCoponent
import React,{ useState, useEffect, useCallback } from 'react';
import { Button, Typography, Grid, TextField, MenuItem, Menu, IconButton, Link, TableBody, TableRow, TableCell, Paper, makeStyles } from '#material-ui/core';
import Modals from './Components/Modals';
import DropDownComponent from './Components/DropDownComponent';
import PropTypes from 'prop-types';
import { fetchImage,postImage, removeImage, updateImageStatus } from './app/ActionCreator';
import { connect } from 'react-redux';
import EditStatusComponent from './Components/EditStatusComponent';
import { Edit, Delete } from '#material-ui/icons';
import TableComponent from './Components/TableComponent';
const styles = makeStyles(theme => ({
root:{
marginTop:'60px',
},
typo:{
flexGrow:1,
textAlign:'center',
marginTop:'30px',
color:'black'
},
headingpaper:{
width:'100%',
height:40,
marginTop:'30px',
background:'white',
borderColor:'white',
},
buttonColor:{
color:'white',
background:'#143174'
},
formControl: {
margin:'8px',
minWidth: 120,
},
selectEmpty: {
marginTop: '16px',
},
formRoot:{
margin:'8px',
width: '25ch',
},
modalButton:{
color:'white',
background:'#143174',
marginRight:'10px'
}
}))
const headCells = [
{id:1,label:'SlNo'},
{id:2,label:'Virtual Machine Image '},
{id:3,label:'createdAt'},
{id:4,label:'createdBy'},
{id:5,label:'IpAddress'},
{id:6,label:'Status'},
{id:7,label:'Action',disableSorting:true}
]
function AdminImages(props){
const [imageModal,setImageModal] = useState(false)
const [name,setName] = useState('')
const [cpu,setCpu] = useState('')
const [ram,setRam] = useState('')
const [disk,setDisk] = useState('')
const [imageid,setImageid] = useState('')
const [anchorEl,setAnchorEl] = useState(null)
const [filterFn, setFilterFn] = useState({ fn: items => { return items; } })
const handlecpuChange = (event) => {
setCpu(event.target.value)
}
const handleramChange = (event) => {
setRam(event.target.value)
}
const handlediskChange = (event) => {
setDisk(event.target.value)
}
useEffect(() => {
props.fetchImageData()
},[])
const handleMenu = useCallback((event,id) => {
setAnchorEl(event.currentTarget)
setImageid(id)
},[])
const handleClose = (ev) => {
setAnchorEl(null)
if(ev.target.innerText !== ''){
const data = {
"status":ev.target.innerText
}
props.updateImageStatusData(imageid,data)
}
}
const newInstance = () => {
if(name !== '' && cpu !== '' && ram !== '' && disk !== ''){
props.postImageData(name,cpu,ram,disk)
}
else {
alert("Please Enter All Field")
}
setImageModal(false)
}
const labstatus = (status) => {
if(status === 'Resuming' || status === 'Running'){
return(
<EditStatusComponent
status={status}
statuscolor='#32CD32'
showDropDown={false}/>
)
}
else if(status === 'Suspending' || status === 'suspending' || status === 'error'){
return(
<EditStatusComponent
status={status}
statuscolor='red'
showDropDown={false}/>
)
}
else{
return(
<EditStatusComponent
status={status}
statuscolor='#143174'
showDropDown={false}/>
)
}
}
const handleSearch = e => {
let target = e.target;
setFilterFn({
fn: items => {
if (target.value == "")
return items;
else
return items.filter(x => x.instance_name.toLowerCase().includes(target.value))
}
})
}
const classes = styles()
const { TblContainer, TblHead, TblPagination, recordsAfterPagingAndSorting } = TableComponent(props.Images.images,headCells,filterFn)
return(
<div className={classes.root}>
<Grid container direction="row" justify="flex-end" alignItems="flex-start">
<Button variant="contained" className={classes.buttonColor} onClick={() => setImageModal(true)}>
Create Images
</Button>
</Grid>
<Typography variant="h2" noWrap className={classes.typo}>
Virtual Machine Image
</Typography>
<Paper>
<TblContainer>
<TblHead/>
<TableBody>
{
recordsAfterPagingAndSorting().map((v,i) => (
<TableRow key={v.id}>
<TableCell>{v.id}</TableCell>
<TableCell>{v.instance_name}</TableCell>
<TableCell>{v.created_at}</TableCell>
<TableCell>{v.created_by.first_name + v.created_by.last_name}</TableCell>
<TableCell><Link target={"_blank"} onClick={() => window.open(`http://${v.ip_address}/project`,'_blank')}>{v.ip_address}</Link></TableCell>
<TableCell>{labstatus(v.status)}</TableCell>
<TableCell>
<div style={{display:'flex'}}>
<IconButton
aria-controls="simple-menu"
aria-haspopup="true"
onClick={(e)=> handleMenu(e,v.id)}>
<Edit/>
</IconButton>
<IconButton onClick={() => props.removeImageData(v.id)}>
<Delete/>
</IconButton>
</div>
</TableCell>
</TableRow>
))
}
</TableBody>
</TblContainer>
<TblPagination/>
</Paper>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}>
<MenuItem value="Resume" onClick={handleClose}>Resume</MenuItem>
<MenuItem value="Suspend" onClick={handleClose}>Suspend</MenuItem>
<MenuItem value="Freeze" onClick={handleClose}>Freeze</MenuItem>
</Menu>
<div>
<Modals
show={imageModal}
title="Create Instance"
handleClose={() => setImageModal(true)}
showSubmit={true}
onPress={newInstance}
size="xs">
<TextField
id="standard-basic"
label="Name"
style={{width:'50ch',margin:10}}
value={name}
onChange={(e) => setName(e.target.value)}
InputLabelProps={{
shrink: true,
}}/>
<DropDownComponent
dropdownid="standard-select-cpu"
selectedValue={cpu}
handleChange={handlecpuChange}
dropdownLabel="CPU">
<MenuItem value="8">8</MenuItem>
<MenuItem value="10">10</MenuItem>
<MenuItem value="12">12</MenuItem>
<MenuItem value="14">14</MenuItem>
<MenuItem value="16">16</MenuItem>
<MenuItem value="18">18</MenuItem>
<MenuItem value="20">20</MenuItem>
<MenuItem value="22">22</MenuItem>
<MenuItem value="24">24</MenuItem>
<MenuItem value="26">26</MenuItem>
<MenuItem value="28">28</MenuItem>
<MenuItem value="30">30</MenuItem>
<MenuItem value="32">32</MenuItem>
</DropDownComponent>
<DropDownComponent
dropdownid="standard-select-ram"
selectedValue={ram}
handleChange={handleramChange}
dropdownLabel="RAM">
<MenuItem value="16">16</MenuItem>
<MenuItem value="20">20</MenuItem>
<MenuItem value="24">24</MenuItem>
<MenuItem value="28">28</MenuItem>
<MenuItem value="32">32</MenuItem>
<MenuItem value="36">36</MenuItem>
<MenuItem value="40">40</MenuItem>
<MenuItem value="44">44</MenuItem>
<MenuItem value="48">48</MenuItem>
<MenuItem value="52">52</MenuItem>
<MenuItem value="56">56</MenuItem>
<MenuItem value="60">60</MenuItem>
<MenuItem value="64">64</MenuItem>
</DropDownComponent>
<DropDownComponent
dropdownid="standard-select-disk"
selectedValue={disk}
handleChange={handlediskChange}
dropdownLabel="Disk">
<MenuItem value="50">50</MenuItem>
<MenuItem value="100">100</MenuItem>
<MenuItem value="150">150</MenuItem>
<MenuItem value="200">200</MenuItem>
<MenuItem value="250">250</MenuItem>
<MenuItem value="300">300</MenuItem>
<MenuItem value="350">350</MenuItem>
<MenuItem value="400">400</MenuItem>
<MenuItem value="450">450</MenuItem>
<MenuItem value="500">500</MenuItem>
</DropDownComponent>
</Modals>
</div>
</div>
)
}
const mapStateToProps = state => {
return {
Images:state.Images,
}
}
const mapDispatchToProps = dispatch => ({
fetchImageData:() => {
dispatch(fetchImage())
},
postImageData:(name,cpu,ram,storage) => {
dispatch(postImage(name,cpu,ram,storage))
},
removeImageData:(id) => {
dispatch(removeImage(id))
},
updateImageStatusData:(id,data) => {
dispatch(updateImageStatus(id,data))
}
})
AdminImages.propTypes = {
classes:PropTypes.object.isRequired,
Images:PropTypes.object,
fetchImageData:PropTypes.func,
postImageData:PropTypes.func,
removeImageData:PropTypes.func,
updateImageStatusData:PropTypes.func,
}
export default connect(mapStateToProps,mapDispatchToProps)(AdminImages)
You can prevent unnecessary re-renders of a child component in 2 ways
If your component is class based then you can extend React.PureComponent or implement shouldComponentUpdate lifecycle method by yourself.
class MyComponent extends React.PureComponent {
// your component logic
}
class MyComponent extends Component {
shouldComponentUpdate(nextProps, nextState) {
//compare nextProps and this.props
//compare nextState and this.state
//return true for re-render, otherwise false
}
// your component logic
}
If you have a functional component you can wrap it with memo function that will check only for prop changes or you can pass it a function in the second argument that will do the current props and next props comparison and return true/false if you want do manual comparison just like shouldComponentUpdate
const MyComponent = React.memo(function(props) {
/* render using props */
});
function MyComponent(props) {
/* render using props */
}
function areEqual(prevProps, nextProps) {
/*
return true if passing nextProps to render would return
the same result as passing prevProps to render,
otherwise return false
*/
}
export default React.memo(MyComponent, areEqual);
Here are some useful links from the docs:
class component
function component
I'm trying to use S3 service to upload an image and it's telling me that certain variables aren't defined when I have defined them. I have imported axios and all the other stuff that are required
import React, { useState } from "react";
import ReactDOM from "react-dom";
import axios from "axios";
import Grid from "#material-ui/core/Grid";
import Button from "#material-ui/core/Button";
import Card from "#material-ui/core/Card";
import TextField from "#material-ui/core/TextField";
import CreateIcon from "#material-ui/icons/Create";
import Box from "#material-ui/core/Box";
import CardMedia from "#material-ui/core/CardMedia";
import MuiAlert from "#material-ui/lab/Alert";
import Snackbar from "#material-ui/core/Snackbar";
import { withStyles } from "#material-ui/core/styles";
import { makeStyles } from "#material-ui/core/styles";
import Chip from "#material-ui/core/Chip";
import Avatar from "#material-ui/core/Avatar";
import Slider from "#material-ui/core/Slider";
import Typography from "#material-ui/core/Typography";
import InputAdornment from "#material-ui/core/InputAdornment";
import { connect } from "react-redux";
function mapStateToProps(state) {
return {};
}
const useStyles = makeStyles((theme) => ({
root: {
"& > *": {
margin: theme.spacing(1),
marginLeft: theme.spacing(15),
},
},
input: {
display: "none",
},
}));
const useSliderStyles = makeStyles({
root: {
width: 250,
},
input: {
width: 100,
},
});
const UploadButton = () => {
const classes = useStyles();
return (
<div className={classes.root}>
<input
accept='image/*'
className={classes.input}
id='contained-button-file'
multiple
type='file'
/>
<label htmlFor='contained-button-file'>
<Button variant='contained' color='primary' component='span'>
Upload
</Button>
</label>
</div>
);
};
const StyledCard = withStyles({
root: { height: 600, width: 350 },
})(Card);
const PetitionForm = () => {
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [open, setOpen] = useState(false);
const [petition, validPetition] = useState(false);
const [noTitle, titleError] = useState(false);
const [noDescription, descriptionError] = useState(false);
const [hashtag, setHashtag] = useState("");
const [arrayOfHashtags, addHashtag] = useState([]);
const [money, setMoney] = React.useState(500);
const slider = useSliderStyles();
const handleTitleChange = (event) => setTitle(event.target.value);
const handleDescriptionChange = (event) => setDescription(event.target.value);
const handleClose = (event, reason) => {
if (reason === "clickaway") {
return;
}
};
const Alert = (props) => (
<MuiAlert elevation={6} variant='filled' {...props} />
);
const clearField = (event) => {
setOpen(true);
if (title.length > 0 && description.length > 0) {
validPetition(true);
setTitle("");
setDescription("");
addHashtag([]);
setHashtag("");
axios({
url: `call/s3/backend`,
method: "post",
data: {
images: imageArray.toByteArray(),
},
})
.then((res) => {
imageUrlArr = res.data;
axios({
url: `api/petition_posts`,
method: "post",
data: {
petition_post: {
title: title,
description: description,
hashtags: arrayOfHashtags.join(" "),
amount_donated: 0,
media: imageUrlArr,
goal: money,
card_type: "petition",
org_profile_id: 1,
},
},
})
.then((res) => {
console.log(res.data);
})
.catch((error) => console.log(error));
})
.catch((error) => console.log(error));
}
titleError(true ? title.length === 0 : false);
descriptionError(true ? description.length === 0 : false);
};
const handleDelete = (h) => () => {
addHashtag((arrayOfHashtags) =>
arrayOfHashtags.filter((hashtag) => hashtag !== h)
);
};
const handleHashtagChange = (event) => setHashtag(event.target.value);
const handleSliderChange = (event, newValue) => {
setMoney(newValue);
};
const handleInputChange = (event) => {
setMoney(event.target.value === "" ? "" : Number(event.target.value));
};
const newHashtag = () => {
if (arrayOfHashtags.length < 3) {
addHashtag((arrayOfHashtags) => arrayOfHashtags.concat(hashtag));
} else {
console.log("Too many hashtags");
}
};
const Hashtags = arrayOfHashtags.map((h) => (
<Chip
key={h.length}
size='small'
avatar={<Avatar>#</Avatar>}
label={h}
onDelete={handleDelete(h)}
/>
));
return (
<StyledCard>
<Box mt={1}>
<Grid container justify='center'>
<TextField
id='outlined-multiline-static'
multiline
rows={1}
variant='outlined'
placeholder='Title'
value={title}
onChange={handleTitleChange}
helperText={
open // only displays helper text if button has been clicked and fields haven't been filled
? !noTitle || petition
? ""
: "Can't be an empty field"
: ""
}
/>
</Grid>
</Box>
<Box mt={1}>
<Grid container justify='center'>
<CardMedia title='Petition'>
<UploadButton />
</CardMedia>
</Grid>
</Box>
<div className={slider.root}>
<Typography>Amount to raise</Typography>
<Box>
<Grid container justify='center'>
<Slider
min={500}
max={10000}
value={typeof money === "number" ? money : 0}
onChange={handleSliderChange}
aria-labelledby='input-slider'
/>
<TextField
className={slider.input}
value={money}
onChange={handleInputChange}
InputProps={{
startAdornment: (
<InputAdornment position='start'>$</InputAdornment>
),
}}
helperText={
money < 500 || money > 10000
? "Please enter a value between 500 and 10000"
: ""
}
/>
</Grid>
</Box>
</div>
<Box mt={1} mb={1}>
<Grid container justify='center'>
<TextField
size='small'
inputProps={{
style: { fontSize: 15 },
}}
id='outlined-multiline-static'
multiline
rows={1}
placeholder='Hashtags'
variant='outlined'
value={hashtag}
onChange={handleHashtagChange}
helperText={
arrayOfHashtags.length === 3
? "You reached the maximum amount of hashtags"
: ""
}
/>
<Button color='primary' onClick={newHashtag}>
Create!
</Button>
{arrayOfHashtags.length > 0 ? Hashtags : ""}
</Grid>
</Box>
<Box mt={1} justify='center'>
<Grid container justify='center'>
<TextField
size='small'
inputProps={{
style: { fontSize: 15 },
}}
id='outlined-multiline-static'
multiline
rows={5}
placeholder='Description'
variant='outlined'
value={description}
onChange={handleDescriptionChange}
helperText={
// only displays helper text if button has been clicked and fields haven't been filled
open
? !noDescription || petition
? ""
: "Can't be an empty field"
: ""
}
/>
</Grid>
</Box>
<Box mt={1}>
<Grid container justify='center'>
<Button onClick={clearField}>
<CreateIcon />
Create Petition!
</Button>
{open && petition && (
<Snackbar open={open} autoHideDuration={2000} onClose={handleClose}>
<Alert severity='success'>
You have successfully create a petition!
</Alert>
</Snackbar>
)}
{open && !petition && (
<Snackbar open={open} autoHideDuration={2000} onClose={handleClose}>
<Alert severity='error'>You're missing one or more fields</Alert>
</Snackbar>
)}
</Grid>
</Box>
</StyledCard>
);
};
export default connect(mapStateToProps)(PetitionForm);
This is the error I'm getting, someone mentioned something about the scope and I think that shouldn't matter when I'm trying to assign a value to a variable as far I as know
Line 109:19: 'imageArray' is not defined no-undef
Line 113:11: 'imageUrlArr' is not defined no-undef
Line 123:24: 'imageUrlArr' is not defined no-undef
Search for the keywords to learn more about each error.
I currently have this practice code that uses Context and Memo Hooks API
codesandbox
Here is a code snippet
export const InputContext = createContext()
export const ParentProvider = ({ initialValues, children }) => {
console.log(initialValues)
const [values, setValues ] = useState(initialValues);
const value = {
values,
setValues
}
return <InputContext.Provider value={value}>{children}</InputContext.Provider>
}
What I want is to update the value of array that holds indicators using Context API after I click edit.
The problem is that I can't access the Context after accessing through the memo
You'll need to wrap all components that need access to the context with the provider. Something like this...
ParentProvider.js
import React, { createContext, useState } from "react";
const INITIAL_STATE = [];
export const InputContext = createContext(INITIAL_STATE);
export const ParentProvider = ({ children }) => {
const [values, setValues] = useState(INITIAL_STATE);
React.useEffect(() => {
console.log("[parentprovider.js]::new values", values);
}, [values]);
return (
<InputContext.Provider value={{ values, setValues }}>
{children}
</InputContext.Provider>
);
};
ShowIndicator.js
import React, { memo, useContext, useState } from "react";
import { Button } from "react-bootstrap";
import { InputContext } from "./ParentProvider";
const ShowIndicator = memo(
({ name, context }) => {
const [_name, _setName] = useState(name);
const [text, setText] = useState();
const { values, setValues } = useContext(InputContext);
const editData = e => {
let newValues = [...values];
newValues[values.indexOf(_name)] = text;
setValues(newValues);
_setName(text);
};
const handleTextChange = e => setText(e.target.value);
const renderDatas = () => {
return (
<div key={_name} className="d-flex justify-content-between">
<input
className="d-flex align-items-center"
defaultValue={_name}
onChange={handleTextChange}
/>
<div>
<Button
variant="info"
style={{ marginRight: "10px" }}
onClick={editData}
>
Edit
</Button>
<Button variant="dark">Delete</Button>
</div>
</div>
);
};
return <div style={{ marginBottom: "5px" }}>{renderDatas()}</div>;
},
(prev, next) => prev.value === next.value
);
export default ShowIndicator;
App.js
import React, { useState, useContext } from "react";
import "./styles.css";
import { Form, Button, Container } from "react-bootstrap";
import ShowIndicator from "./ShowIndicator";
import { InputContext } from "./ParentProvider";
function App() {
const [curText, setCurText] = useState("");
const { values, setValues } = useContext(InputContext);
const onSubmit = e => {
e.preventDefault();
if (!values.includes(curText)) {
values ? setValues([...values, curText]) : setValues([curText]);
setCurText("");
}
};
const onChange = e => setCurText(e.target.value);
return (
<div>
<Container style={{ marginTop: "10px", textAlign: "center" }}>
<div>Add Indicator</div>
<Form inline onSubmit={onSubmit} style={{ marginBottom: "1rem" }}>
<Form.Control
style={{ flex: "1 0 0" }}
onChange={onChange}
value={curText}
/>
<Button type="submit" variant="success">
Submit
</Button>
</Form>
{values &&
values.map((data, index) => {
return <ShowIndicator key={index} name={data} />;
})}
</Container>
</div>
);
}
export default App;
index.js
import React from "react";
import App from "./App";
import { render } from "react-dom";
import { ParentProvider } from "./ParentProvider";
render(
<ParentProvider>
<App />
</ParentProvider>,
document.getElementById("root")
);
Using useContext you need to pass the entire context object (not only Consumer). And just use it like this
const Component = () =>{
const context = useContext(InputContext)
const { values, setValues } = context
const handleChange = () => setValues('foo')
return(
<>
{values}
<button onClick={handleChange}>Change</button>
</>
)
}