How to get a state from a different component in React - javascript

I need to disable a button in reactjs depending on a value taken from a different component.
In below component (main.js), I need to disable the Button. But This has to be disabled depending on a value taken from a value another component (upload.js)
export default function HorizontalLabelPositionBelowStepper() {
.
.
.
return (
<Grid container>
<Grid item lg="12" md="12" xs="12">
<div className={classes.root}>
<div>
<Grid container justify="center">
<Grid item md="1">
<Button // button I need to disable
variant="contained"
color="secondary"
onClick={handleNext}
endIcon={<NavigateNext />}
>
{activeStep === steps.length - 1 ? "Finish" : "Next"}
</Button>
</Grid>
</Grid>
</div>
</div>
</Grid>
</Grid>
);
}
And this is the upload.js
// imports...
.
.
.
export default function ElevateAppBar(props) {
const [file, setFile] = useState('');
const [filename, setFileName] = useState('eg: employees.csv'); // this filename value should be passed to the component `main.js`
return (
<Grid container justify="flex-start">
<Grid container item lg="12" md="12">
<Grid item lg="4" md="4" xs="4">
<Form>
<Form.File id="custom-file" label={filename} custom />
</Form>
</Grid>
</Grid>
<Grid container justify="center">
<Button variant="contained" style={{backgroundColor: "lightgreen"}} endIcon={<CloudUpload />}>
Upload
</Button>
</Grid>
</Grid>
);
}
In the upload.js I have a variable with the name filename. I need to pass this value to the first component - that is main.js
so the logic could be like something (this is not reactjs syntaxes, this is just to give the idea on what I need to do):
if(filename_taken_from_upload_js == "eg: employees.csv"){
// disable the button
}else{
// enable the button
}
Can someone help, Please?

This could be a case for lifting state up : https://reactjs.org/docs/lifting-state-up.html#lifting-state-up
If this value could be used by other unrelated components, you should consider putting it in a global state, using Redux or Context API.
https://redux.js.org/faq/react-redux#react-redux
https://reactjs.org/docs/context.html#when-to-use-context

Related

How to set a value from an external API to an input field using formik and MUI?

I'm trying to enter a value from an API into a TextField MUI component that uses formik. The idea is that the form is one component and I'm using it into another component in which if a value comes from an API, then it is passed as prop to the form component, if it is not passed, then the TextField component is rendered in order to enter the value manually.
This is the code snippet from the form component, in which if I have a prop passed the value is set to value and the onChange prop of the TextField is managed to set update the value received into the formik input using setFieldValue. I have also enabled the enableReinitialize prop on formik. All of this in order to make the isValid formik prop recognize the value as valid and enable the submit button.
// FormComponent
onst valSchema = Yup.object().shape({
batchNo: Yup.string()
.required('Required'),
item: Yup.string().required('Required'),
price: Yup.string().required('Required'),
});
const ItemForm = (props) => {
return (
<Grid container>
<Grid item xs={12}>
<Container maxWidth="md">
<div>
<Formik
enableReinitialize
initialValues={initialValues}
validationSchema={valSchema}
onSubmit={(values) => {
localHandleSubmit(values);
}}
>
{({ dirty, isValid, errors, handleChange, setFieldValue }) => (
<Form>
<Grid container spacing={2}>
{props.batchValue ? (
<Grid item xs={12}>
<TextField name="batchNo" label="Batch No" value={props.batchValue} onChange={(e) => {
handleChange(e);
setFieldValue('batchNo', props.batchValue);}} />
</Grid>
) : (
<Grid item xs={12}>
<TextField name="batchNo" label="Batch No" />
</Grid>
)}
<Grid item xs={6}>
<TextField name="item" label="Item" />
</Grid>
<Grid item xs={6}>
<TextField name="price" label="Price" />
</Grid>
<Grid item xs={12}>
<Button
fullWidth
variant="contained"
disabled={!isValid || !dirty}
type="submit"
>
{' '}
ADD
</Button>
</Grid>
</Grid>
</Form>
)}
</Formik>
</div>
</Container>
</Grid>
</Grid>
);
};
This is how I'm passing the value to the form component.
<DialogContent dividers>
<FormComponent batchValue={batch} />
</DialogContent>
The problem I'm having is that the submit button is not enable, thus I found out that the isValid formik prop is false and the error for this value is required, so formik is not recognizing the value passed from the API as valid.
I would like to know how I can make this work.

Full width button only on xs screens

I am using Material UI and I'm creating a form, I have a <Button> component inside that form Grid.
I want the button to take its regular width and height on md screens and above, but want to have it full width and a bit of extra padding-y on xs screens. But I'm not able to figure out how?
Full code: or view in codesandbox
import "./styles.css";
import Grid from "#mui/material/Grid";
import Button from "#mui/material/Button";
export default function App() {
return (
<Grid container spacing={2}>
<Grid item xs={12}>
<Button variant="contained">Submit</Button>
</Grid>
{/*
<Grid item xs={12}>
<Button fullWidth variant="contained">
Submit
</Button>
</Grid>
*/}
</Grid>
);
}
I was able to solve this by using the useMediaQuery[1] MUI hook as follows:
Full code: (sorry for not providing a code sandbox, but useMediaQuery doesn't work with the code sandbox for some reason)
import "./styles.css";
import Grid from "#mui/material/Grid";
import Button from "#mui/material/Button";
import useMediaQuery from "#mui/material/useMediaQuery";
export default function App() {
const isFullWidth = useMediaQuery((theme) => theme.breakpoints.only("sm"));
return (
<Grid container spacing={2}>
<Grid item xs={12}>
<Button fullWidth={isFullWidth} variant="contained">
Submit
</Button>
</Grid>
</Grid>
);
}
I don't know why all that was actually required, we should have some syntax as follows:
// this is not real, but just an example
<Button fullWidth={(theme) => theme.breakpoints.is("sm")} variant="contained">
anyways.

Unable to pass down useState props for adding count

I have three components im trying to connect together to add the number of items I have in a shopping cart. I have the main component which creates the CartCount object and setCartCount functionality:
function MinerStore() {
const [cartCount, setCartCount] = useState([0]);
const addToCart = () => {
setCartCount(cartCount + 1);
};
return (
<>
<div >
<StoreNav cartCount={cartCount} />
<div>
<StoreCard addToCart={addToCart} />
</div>
</div>
</>
);
}
I also then have the two components I am trying to show the added count on as well as the added count functionality:
export default function StoreCard(addToCart) {
return (
<ThemeProvider theme={theme}>
<main>
<Container>
<Grid container spacing={3}>
{data.map((data) => (
<Grid item key={data.id} xs={12} sm={6} md={3}>
<Card>
<CardContent>
</CardContent>
<CardActions>
<div>
<Button onClick={addToCart}>
Add to Cart
</Button>
</div>
</CardActions>
</Card>
</Grid>
))}
</Grid>
</Container>
</main>
</ThemeProvider>
);
}
export default function StoreNav(cartCount) {
return (
<AppBar position="static">
<Toolbar>
<IconButton>
<Badge badgeContent={cartCount} color="primary">
<ShoppingCartIcon />
</Badge>
</IconButton>
</Toolbar>
</AppBar>
);
}
You've set your initial state as an array [0].
Set it as as 0 and check it out
And ofc as the people above mentioned you should either use props or {addToCart} in the child's parameter.
Then on button should look as such:
<Button onClick={addToCart /* or props.addToCart */}>Add to Cart </Button>

ReactJS- Calling a dynamic link based on a prop value

i have a cards.js which has the following function. Now the calling method passes three values . header1, header2 and Link (this is the *.js page that has to be called)
export default function Cards(props) {
const classes = useStyles();
return (
<div className="App">
<Card className={classes.card} variant="outlined">
<CardContent>
<Typography
variant={"h5"}
gutterBottom
>
{props.header1}
</Typography>
<Typography
variant={"caption"} >
{props.header2}
</Typography>
<Divider className={classes.divider} light />
<Button variant="contained" color="secondary" onClick={()=> (call either link1.js or link2.js )}>
Lets goooooo!
</Button>
</CardContent>
</Card>
</div>
);
}```
calling method
class UIM extends Component {
render() {
return (
<div className="text-page">
<Grid container alignItems="center" justify="space-evenly" >
<Grid ><Cards header1="card 1 for a task1" header2="click text2" link="link1"/></Grid>
<Grid ><Cards header1=" card 2 for task 2" header2=" click text 2" link="link2"/></Grid>
</Grid>
</div>
)
}
}
I am new to react and learning right now, so wondering if such a thing is possible or should i use router to route to different pages (ie. call either link1.js or link2.js ) ??
I found out by by adding pathname:props.link i can get the button to link to multiple locations within the material ui button.
<Card className={classes.card} variant="outlined">
<CardContent>
<Typography
variant={"h5"}
gutterBottom
>
{props.header1}
</Typography>
<Typography
variant={"caption"} >
{props.header2}
</Typography>
<Divider className={classes.divider} light />
<Button variant="contained" color="secondary">
<NavLink to={{pathname:props.link}}>Lets goooooo!</NavLink>
</Button>
</CardContent>
</Card>

Error when passing a functional component to a class component as a component prop

I create a functional component and a class component.
In the render method I want to call my functional component as a component prop.
My code:
function projectList(props) {
const { classes } = props
return (
<div>
{projects.slice(0, 5).map(project => (
<Card className={classes.card} key={project.id}>
<CardHeader
classes={{ title: classes.h6, subheader:
classes.subtitle1 }}
title={project.projectName}
subheader={project.h5}
/>
<CardActionArea>
<CardMedia
component="img"
alt={project.h5}
className={classes.media}
image={project.img}
/>
<CardContent>
<Typography paragraph>
{project.paragraph}
</Typography>
</CardContent>
</CardActionArea>
<CardActions className={props.classes.actions}>
<Button
className={props.classes.projectButton}
component={Link}
to={project.pathname}
size="medium"
color="secondary"
>
Project Details
</Button>
</CardActions>
</Card>
))}
</div>
)
}
projectList.propTypes = {
classes: PropTypes.any.isRequired, // eslint-disable-line
}
class Projects extends Component {
static propTypes = {
classes: PropTypes.any.isRequired, // eslint-disable-line
}
render() {
const { classes } = this.props
return (
<Grid container className={classes.projectPage}
direction="row">
<Grid item xs />
<Grid item>
<Grid container alignItems="center" direction="column">
{/* Title */}
<Grid item>
<Typography className={classes.h5} variant="h5">Latest
Projects</Typography>
</Grid>
<Grid item xs={12} sm={6} component={projectList} />
<Grid item className={classes.nextButton}>
<Button
className={classes.contained}
size="medium"
variant="contained"
onClick={this.handleShowMore}
>
See More
</Button>
</Grid>
</Grid>
</Grid>
<Grid item xs />
</Grid>
)
}
}
export default withStyles(styles)(Projects)
I have this error message appearing
index.js:1452 Warning: Failed prop type: The prop classes is marked as required in projectList, but its value is undefined.
Anyone could help me fix this ?
Not sure where that <Grid/> component comes from but you could pass an inline wrapper for projectLists and return it as a jsx element (you would need a capital first letter though):
<Grid item xs={12} sm={6} component={() => <ProjectList classes={classes} />} />
Don't forget to change the decleration to capital letter:
function ProjectList(props) {
...

Categories

Resources