When clicking a button i have an event error, the problem is i dont know how to convert this with hooks
const Header = () => {
const [value, setValue] = React.useState(0);
function handleChange(event, newValue) {
setValue(newValue);
}
function onLogoutClick(e) {
e.preventDefault();
this.props.logoutUser();
}
//this.onLogoutClick = this.onLogoutClick.bind(this);
return (
<div className={classes.root}>
<AppBar position="sticky">
<Tabs value={value} onChange={handleChange} centered>
{" "}
{/* <Tabs value={value} onChange={handleChange}>
{sections.map(section => (
<Tab label={section} />
))}
</Tabs> */}{" "}
<Tab label="Indicadores Globais" />
<Tab label="Indicadores Colaboradores" />
<Tab label="Indicadores Produto" />
<Tab label="Indicadores Temporais" />
<Button
color="inherit"
className={classes.classesButton}
onClick={onLogoutClick}
>
Logout
</Button>
TypeError: Cannot read property 'props' of undefined when clicking on the button. I know the problem is on the onClick={onLogoutClick} but im not sure how to solve this. Any help?
An event handler will override this in the callback with the event object. To make sure the component is scoped in the callback, you need to bind it to the function.
<Button
color="inherit"
className={classes.classesButton}
onClick={onLogoutClick.bind(this)}
>
this inside onLogoutClick points to the html element. If you want to access the component as this, You need to bind the action with this.
<Button
color="inherit"
className={classes.classesButton}
onClick={onLogoutClick.bind(this)}
>
But this is not the recommended method as it binds this with action every time component renders. The recommended method is to bind the action with this in the constructor.
constructor(props) {
super(props);
this.state = {
};
this.onLogoutClick = this.onLogoutClick.bind(this);
}
Related
How do i pass a validation value from child component to parents component?
i tried to use props but it didn't work . i tried to pass the 'isValidValue' status
Child Component :
function MilikSendiri({isValidValue}) {
const { register, handleSubmit } = useForm()
function sweetAlertclick(){
Swal.fire({
icon: 'success',
title: 'Data anda sudah tersimpan ',
})
}
return (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={onSubmit}
// validateOnMount
>
{
formik => {
const isValidValue = formik.isValid? ("Data Completed") : ("DData incomplete");
return(
<div>
<div>
Status : {isValidValue}
<label htmlFor="luasTanah"> Luas Tanah </label>
<Field className="formBiodata"
type="text" id="outlined-basic"
placeholder="luasTanah"
fullWidth
id="luasTanah"
name="luasTanah"
margin="normal" variant="outlined"
/>
<ErrorMessage name='luasTanah' component={TextError}/>
</div>
<div>
<label htmlFor="BiayaPBB"> Biaya PBB </label>
<Field className="formBiodata"
type="text" id="outlined-basic"
placeholder="BiayaPBB"
fullWidth
id="BiayaPBB"
name="BiayaPBB"
margin="normal" variant="outlined"
/>
<ErrorMessage name='BiayaPBB' component={TextError}/>
</div>
<Button onClick={sweetAlertclick} type ="submit"
variant="contained" startIcon={<SaveIcon />} color="primary" style={{
marginLeft: '25rem', marginTop: '20px', width: '20rem', height: 45,
fontSize: 22, backgroundColor: '#22689F'}}
disabled={!formik.isDirty && !formik.isValid} >Simpan
</div>
)
}
}
</Formik>
)
}
Parent Component :
function UKTRumah ({isValidValue}) {
return (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={onSubmit}
// validateOnMount
>
{
formik => {
console.log('Formik props', formik)
return(
<div className ="IsiBiodata">
<Accordion square expanded={expanded === 'panel1'} onChange=.
{handleChange('panel1')} style={{marginLeft: '15rem', marginRight:
'15rem', marginTop: '3rem'}}>
<AccordionSummary aria-controls="panel1d-content" id="panel1d-
header">
<PersonIcon/>
<Typography> Data Rumah</Typography>
<Typography}> { isValidValue }
</Typography>
</AccordionSummary>
<AccordionDetails>
<div className ="IsiBiodata">
<Form>
</div>
</Form>
</div>
</AccordionDetails>
</Accordion>
</div>
)}}
</Formik>
)}
Thank you
Your example code seems to be lacking some key lines to answer the question specifically.
However, generally if it is data that Parent should be aware of, but that the child will make use of, it should be a value of state in the parent, then handed to the child as props. Here's a very small example using functional components:
const Child = ({ formik, setIsValid, isValid }) => {
useEffect(() => {
setIsValid(formik.isValid)
}, [formik.isValid]);
return <input />;
}
const Parent = () => {
const [isValid, setIsValid] = useState(true);
return <Child isValid={isValid} setIsValid={setIsValid} />
}
You can hold the value on your parent and pass a function to change it to your child. I can't really show you that with the code you posted, but I can show an example of what I mean. The parent has a state with an update function setIsValid and passes that to the child. The child can call setIsValid and that will update the isValid value on the parent.
parent
function Parent() {
const [isValid, setIsValid] = useState(false);
return <div>
<Child setIsValid={setIsValid} />
IsValid {isValid}
</div>
}
child
function Child({ setIsValid }) {
return <button onClick={() => setIsValid(true)}>Set Valid</button>
}
So, I'm trying to create a signup form for a web-app - but are running into a few issues.
I'm using hooks with a function to render signup page, which I'm routing to from the login page.
It works fine assuming I return the html directly from the return in the function (signup), but once the signup has been engaged, I wish swap the form for an acknowledge of it being send.
From what I can tell, people simply wrap each html in an arrow function and then toggles between using a bool or similar. But that's where the issues arrive.
TLDR;
One of the signup textfields autocompletes, fetching from an API. The API then saves the content in a hook variable (address). The second I update the address variable, the form seem to reset - cursor going to the first inputfield.
This only happens when I wrap the html in components, not if I insert all the html in the (signup) return.
I tried to clean it up a bit, but the code more or less look like this.
Any help or pointers would be great :)
export default function SignUp(props)
{
const [activeStep, setActiveStep] = React.useState(0);
const [addresses, setAddresses] = React.useState([{ tekst: '' }]);
const APICall = async (e) =>
{
e.preventDefault();
// Fetchs JSON and set Addresses hook
}
const handleSubmit = props => form =>
{
form.preventDefault()
setActiveStep(activeStep + 1);
}
const CreateAccount = (e) =>
{
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Opret konto
</Typography>
<form className={classes.form} noValidate
onSubmit={handleSubmit(props)}>
<Autocomplete
id="address"
options={addresses}
getOptionLabel={(option) => option.tekst}
style={{ width: 300 }}
renderInput={(params) =>
<TextField {...params} label="Combo box" variant="outlined" onChange={userTest} />
}
/>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Sign Up
</Button>
</form>
</div>
<Box mt={5}>
<Copyright />
</Box>
</Container>
);
}
const CreateAccountACK = () =>
{
return (
<React.Fragment>
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Nyt konto oprettet!
</Typography>
<Button
type="button"
variant="contained"
color="primary"
className={classes.submit}
onClick={() => { props.history.push('/') }}
>
Ok
</Button>
</div>
<Box mt={8}>
<Copyright />
</Box>
</Container>
</React.Fragment>
);
}
return (
<div>
{activeStep == 0 ? <CreateAccount /> : <CreateAccountACK />}
</div>
)
}
Got it working by breaking each function into its own functional component, then render these from the main class (signup) using conditionals. Swapping between pages are handled by callback to "handlesubmit" in this function. Pass history as the final page routes back to main. Feel like this isn't the best way of doing this tho =D, but it avoids the issues with re-renders while typing.
So now the Signup just return ...
export default function SignUp(props)
{
const [activeStep, setActiveStep] = React.useState(0);
const handleSubmit = props => form =>
{
form.preventDefault()
console.log(form)
setActiveStep(activeStep + 1);
}
return (
<div>
{activeStep == 0 ? <CreateAccount handleSubmit={handleSubmit} /> : <CreateAccountACK handleSubmit={handleSubmit} history={props.history}/>}
</div>
)
}
And each function, hooks/variables exist in their own file/function.
I have a React app which uses Material UI for it's interface. I've created a custom button component which styles the default Material UI button and also uses redux.
The render() function of my button component looks like this:
return (
<div className={classes.buttonWrapper}>
<Button
ref={this.props.innerRef}
disabled={loading || disabled}
onClick={this.handleClick}
{...other}>
<React.Fragment>
{children}
{this.buildLoader(loading, classes)}
</React.Fragment>
</Button>
</div>
);
What I want is to be able to include this button on a page and have the UI trigger its click event by other means other than clicking on it. For example, on a login form I want a user who currently has focus on the password textbox to be able to trigger the button click by hitting the Return/Enter key.
I'm sure I need to use the concept of forwarding refs in React, but I'm fairly new to React and can't get it working. You can see on my button I've defined a ref set to this.props.innerRef. My button component (called WaitingButton) is exported like this:
const withInnerRef = React.forwardRef((props, ref) => <WaitingButton
innerRef={ref} {...props}
/>);
var component = withStyles(styles)(withInnerRef);
export default connect(mapStateToProps, mapDispatchToProps)(component);
I've then added this button to a form like this:
<Paper>
<TextField
style={{marginBottom: '8px'}}
label="A textbox!"
fullWidth
onKeyPress={(e) => { if (e.key === "Enter") this.triggerClick(); }} />
<WaitingButton
ref={this.submitButton}
variant="contained"
color="primary"
onClick={(e) => {
console.log('Button clicked :)', e.target);
}}>
Press enter in textbox!
</WaitingButton>
</Paper>
See I've assigned the button's ref and in this page's constructor I've initialised the ref in the constructor using this.submitButton = React.createRef();
Finally the triggerClick looks like this:
triggerClick() {
console.log('CLICK', this.submitButton.current);
this.submitButton.current.click();
}
When I hit enter in the textbox, I can inspect the value assigned to this.submitButton.current and can see it is the Redux connect object that I've wrapped my button with. However, I also get the error this.submitButton.current.click is not a function so clearly the ref isn't getting forwarded all the way to the button itself.
I'm afraid I'm a bit lost so appealing for your help!
Just want to ensure, what you want is: when user press Enter while typing on the textfield, the button will show a loading visual, right?
I think you don't have to pass ref to the button component, you could just pass state isLoadingShown into your WaitingButton
WaitingButton.js
return (
<div className={classes.buttonWrapper}>
<Button
ref={this.props.innerRef}
disabled={loading || disabled}
onClick={this.handleClick}
{...other}>
<React.Fragment>
{children}
{this.props.isLoadingShown && this.buildLoader(loading, classes)}
</React.Fragment>
</Button>
</div>
);
Then in the form component
state = {
isLoadingShown: false,
}
triggerClick() {
this.setState({ isLoadingShown: true })
}
render(){
...
<Paper>
<TextField
style={{marginBottom: '8px'}}
label="A textbox!"
fullWidth
onKeyPress={(e) => { if (e.key === "Enter") this.triggerClick(); }} />
<WaitingButton
variant="contained"
color="primary"
isLoadingShown={this.state.isLoadingShown}
onClick={(e) => {
console.log('Button clicked :)', e.target);
}}>
Press enter in textbox!
</WaitingButton>
</Paper>
...
}
don't forget to set isLoadingShown to false again in componentWillUnmount
I just tried to reproduce your case. And I created a codesandbox for it. I think I found the problem. It seems React.forwardRef only works with prop name forwardedRef so try to rename the innerRef property to forwardedRef in your code.
const withInnerRef = React.forwardRef((props, ref) => <WaitingButton
forwardedRef={ref} {...props}
/>);
and also in your render() function
<Button
ref={this.props.forwardedRef}
disabled={loading || disabled}
onClick={this.handleClick}
...
You can try it with my simplified codesandbox https://codesandbox.io/s/intelligent-cori-rb5ce
I'm switching over to building components using Hooks and i'm struggling to setup refs with useRef()
Parent (The ref is only added to one component currently, as I'd like to ensure this is working before extending functionality to others):
export default function UserPanels({ selected_client } ) {
const classes = useStyles();
const [value, setValue] = useState(0);
const container = useRef( null );
function handleChange(event, newValue) {
setValue(newValue);
container.current.displayData();
}
return (
<div className={classes.root}>
<UserTabs
value={value}
onChange={handleChange}
indicatorColor="primary"
textColor="primary"
variant="fullWidth"
aria-label="full width tabs example"
>
<Tab label='Service User' {...a11yProps(0)} />
<Tab label='Care Plan' {...a11yProps(1)} />
<Tab label='Contacts' {...a11yProps(2)} />
<Tab label='Property' {...a11yProps(3)} />
<Tab label='Schedule' {...a11yProps(4)} />
<Tab label='Account' {...a11yProps(5)} />
<Tab label='Invoices' {...a11yProps(6)} />
<Tab label='Notes' {...a11yProps(7)} />
<Tab label='eMAR' {...a11yProps(8)} />
</UserTabs>
<TabPanel value={value} index={0}>
<UserDetailsContainer
selected_client={ selected_client }
/>
</TabPanel>
<TabPanel value={value} index={1}>
Care Plan
</TabPanel>
<TabPanel value={value} index={2}>
<ContactsContainer
ref={ container }
selected_client={ selected_client }
/>
</TabPanel>
<TabPanel value={value} index={3}>
Property
</TabPanel>
<TabPanel value={value} index={4}>
Schedule
</TabPanel>
<TabPanel value={value} index={5}>
Account
</TabPanel>
<TabPanel value={value} index={6}>
Invoices
</TabPanel>
<TabPanel value={value} index={7}>
Notes
</TabPanel>
<TabPanel value={value} index={8}>
eMAR
</TabPanel>
</div>
);
}
Child:
export default function ContactsContainer( props ) {
const [ state, setState ] = useState({
contact_records: contacts,
data_ready: true
});
function displayData() {
console.log( 'display time' );
}
if ( !state.data_ready ) return null
return (
<>
{
state.contact_records.map( ( contact ) => {
return <ContactRecord contact={ contact } />
} )
}
</>
)
}
Essentially, I'm trying to call a child function from the parent but ref.current evaluates to null and when handleChange() is invoked I receive the error container.current is null and I regularly see the error Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
As a note, I've already tested forwardRef:
<ContactsContainer
ref={ container }
selected_client={ selected_client }
/>
And, while this removes the error, it does not solve the issue. I've never had an issue using refs with class components but I seem to be missing something here.
First of all, don't overuse refs ( react doc). You must not control your child component by call directly its functions (and honestly you can't do this with function components).
If you need to display something in your children, you have to prepare data in your parent component and pass that data by props. Children should be as simple as possible. They should get props and displaying some data.
In parent:
const [value, setValue] = useState(0);
const [time, setTime] = useState(null);
function handleChange(event, newValue) {
setValue(newValue);
setTime('display time');
}
return (
....
<ContactsContainer
time={time}
selected_client={ selected_client }
/>
....
)
If you need to make some side effects (e.g. make HTTP calls, dispatch Redux actions) in your child when props changes, you have to use useEffect hook.
In parent:
<ContactsContainer
value={value}
selected_client={ selected_client }
/>
In child:
useEffect(() => {
console.log('display time action');
}, [props.value]);
you can pass a ref as a prop:
// ...
const infoRef=useRef(null)
useEffect(()=>{
if(infoRef.current) infoRef.current()
},[])
return <ChildComponent infoRef={infoRef} />
and then in child:
useEffect(()=>{
infoRef.current=childFunctionToExecuteInParent
},[])
I am trying to pass a state to a component so i can update it's state whenever i type in a component's text field. However this is not working i am not sure why. Most of the examples I've found online were dealing with the similar problems on the same class. However i need to juggle this state between components.
Not only the state doesn't change but if i add the "value={information}" part in the textfield it doesn't let me type.
Here is an example of the code.
Class that uses the component:
class SomeClass extends Component {
state = {
information: '',
};
handleInfoChange(event) {
this.setState({
information: event.target.value,
});
}
render(){
return(
<div>
<TesteComponent
information={this.state.information}
handleInfoChange={this.handleInfoChange}
/>
Component code:
const TesteComponent = ({information}, handleInfoChange) => (
<Dialog
disableEscapeKeyDown
disableBackdropClick
>
<DialogContent>
<DialogContentText>
<p>Emails:</p>
<TextField value={information} className="bootstrapInput" onChange={() => handleInfoChange}/>
</DialogContentText>
</DialogContent>
</Dialog>
PS: I posted solely the part that is giving me trouble since the component in it's entirety works for the exception of the Onchange method problem i am having.
PS2: I forgot to add handleInfoChange being passed to the component in the question. It ahs been updated now.
TesteComponent doesn't have access to handleInfoChange. You can pass that function as a property like this
<TesteComponent
information={this.state.information}
handleInfoChange={this.handleInfoChange}
/>
and then in TesteComponent change it to
const TesteComponent = (props) => (
<Dialog
disableEscapeKeyDown
disableBackdropClick
>
<DialogContent>
<DialogContentText>
<p>Emails:</p>
<TextField value={props.information} className="bootstrapInput" onChange={() => props.handleInfoChange}/>
</DialogContentText>
</DialogContent>
</Dialog>
Firstly, you are not passing handleInfoChange function to TesteComponent as props
Secondly, you can not destructure and use arguments without destructuring together. You should instead write const TesteComponent = ({information, handleInfoChange}) => ( after passing the handleInfoChange as props
const TesteComponent = ({ information , handleInfoChange }) => (
<Dialog
disableEscapeKeyDown
disableBackdropClick
>
<DialogContent>
<DialogContentText>
<p>Emails:</p>
<TextField value={information} className="bootstrapInput" onChange={() => handleInfoChange}/>
</DialogContentText>
</DialogContent>
</Dialog>
SomeClass
class SomeClass extends Component {
state = {
information: '',
};
handleInfoChange(event) {
this.setState({
information: event.target.value,
});
}
render(){
return(
<div>
<TesteComponent
information={this.state.information}
handleInfoChange={this.handleInfoChange}
/>
)
}
}
class SomeClass extends Component {
state = {
information: ''
};
// changed to arrow function to bind 'this'
handleInfoChange = event => {
this.setState({information: event.target.value});
}
render() {
return(
<div>
<TesteComponent
information={this.state.information}
// pass handleInfoChange as a prop
handleInfoChange={this.handleInfoChange}
/>
</div>
);
}
}
const TesteComponent = ({information, handleInfoChange}) => (
<Dialog disableEscapeKeyDown disableBackdropClick>
<DialogContent>
<DialogContentText>
<p>Emails:</p>
<TextField
className="bootstrapInput"
value={information}
onChange={handleInfoChange}
/>
</DialogContentText>
</DialogContent>
</Dialog>
);
first of all you should bind your click event and set in state and here i am going to print change value in console ....
here is my code try this one....
class SomeClass extends Component {
state = {
information: '',
};
this.handleInfoChange= this.handleInfoChange.bind(this);
handleSubmit = event => {
event.preventDefault();
}
handleInfoChange(event) {
this.setState({
information: event.target.value,
console.log(this.state.information);
});
}
render(){
return(
<div>
const TesteComponent = ({information}, handleInfoChange) => (
<Dialog
disableEscapeKeyDown
disableBackdropClick
>
<form onSubmit={this.handleSubmit}>
<DialogContent>
<DialogContentText>
<p>Emails:</p>
<TextField value={information} className="bootstrapInput" onChange={this.handleInfoChange}/>
</DialogContentText>
</DialogContent>
</Dialog></div></form>