Missing this in onSubmit event handler with redux-form 6 - reactjs

I am trying to use my componenet props to call an action in submit event handler with redux-form 6.1.1 but in the event handler function I get "this" is undefined. here is what I do:
class ForgetPasswordForm extends Component {
xubmit (values) {
console.log(this);
}
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={ handleSubmit(this.xubmit) }>
...
);
};
}
I also tried () => handleSubmit(this.xubmit.bind(this)) and this.xubmit.bind(this) as mentioned in React: this is null in event handler but none of them worked out.
Here is more details about my setup:
boilerplate: create-react-app v.0.5
react: v.15.3.2
redux: v.3.6

handleSubmit.bind(this.xubmit) by this way inside the handleSubmit, this points to this.xubmit.
I also suggest you to read how bind works.
Note: I know only JavaScript

Since you didn't provide your handleClick function I can only imagine that you have directly called the function passed in like handleClick (fn) { fn() } and this way you would have access to the context in that fn. Also you should pass a function to event handlers. Do something like this and see if it works:
onSubmit={this.props.handleSubmit.bind(this.props.context, this.xubmit.bind(this))}
You need to send parent component's context to your ForgetPasswordForm and bind handleSubmit to it to have access to that parent component's context and bind this.xubmit to this in order for it not to be undefined.

your custom submit function basically is wrong , just edit your code this way.
class ForgetPasswordForm extends Component {
xubmit = (values) => {
console.log(this);
}
}
an call it this way ,
<form onSubmit={handleSubmit((values) => this.xubmit(values)) }>

you must use class properties and arrow function together
class properties is part of stage-2
class ForgetPasswordForm extends Component {
xubmit = (values) => {
console.log(this);
}
render() {
const { handleSubmit } = this.props;
return <form onSubmit={ handleSubmit(this.xubmit) }>;
};
}
Arrow function bind this to function

Related

Implementing events in a REACT function component

I am trying to figure out how to implement my own custom events. I asked the question here but the word event seems to confuse my question. I was asked to add a new question, so I will try to do my best in another way:
Related post
My component:
import React, { useState } from "react";
const DropdownPaging2 = props => {
function myClickFunc(val) {
alert("WHAT SHOULD I ADD HERE TO FIRE MY EVENT TO THE CONSUMING COMPONENT");
}
return <div onClick={() => myClickFunc(100)}>CLICK me</div>;
};
export default DropdownPaging2;
Using my component in another components (comsuming component) render function:
<DropdownPaging2></DropdownPaging2>
I would like implement so I can pass a new event to the consuming component. Something lige this:
<DropdownPaging2 myCustomEvent={() => myCustomEvent(100)}></DropdownPaging2>
You can pass functions as props to your DropdownPaging2 component like you mentioned:
<DropdownPaging2 myEvent={() => myCustomEvent(100)}></DropdownPaging2>
And then use it in the component like this.
const DropdownPaging2 = props => {
const myClickFunc = (val) => {
if(props.myEvent){
props.myEvent();
} else {
// default if no function is passed
}
}
return <div onClick={() => myClickFunc(100)}>CLICK me</div>;
};
export default DropdownPaging2;
This way you are free to pass a custom function
Just make your component use the custom callback if it was passed as a prob, otherwise use the default one.
return <div onClick={prop.myCustomEvent ? prop.myCustomEvent : () => myClickFunc(100)}>CLICK me</div>;

React useRef Hook with Flow Typings

I'm using React useRef with Flow typings and I'm trying to write a wrapper component for a third party web components library.
The web component expects a changeCallback function and I'm using the ref to assign it to the ref.
function RadioButtonGroup({ onChange, children }) {
const ref: { current: null | ElementRef<ElementType> = React.useRef(null);
React.useEffect(() => {
if (ref.current) ref.current.changeCallback = onChange;
}, [onChange]);
return <web-component ref={ref}>{children}</web-component>
}
Since HTMLElement does not contain a property called changeCallback flow throws an error.
Cannot assign handleChange to ref.current.changeCallback because property changeCallback is missing in HTMLElement
I tried extending "ElementType" with the property like this
ElementRef<ElementType & { changeCallback: Function }>
But this results in the following error:
Cannot instantiate ElementRef because object type [1] is not a React component.
The web component does not fire the "change" event on change. It executes the function changeCallback. Here's the documentation for the library.
// MyComponent.js
class MyComponent extends Component {
constructor() {
// ...
// Create a ref
this.sdxSelectEl = React.createRef();
}
componentDidMount() {
// Attach callback here to ref
this.sdxSelectEl.selectCallback = (selection) => console.log(selection);
}
render() {
// ...
<sdx-select ref={el => (this.sdxSelectEl = el)} />
// ...
}
}
The solution is to call useRef with an explicit type argument to represent the expected type:
const ref = React.useRef<null | (HTMLElement & { changeCallback: Function })>(null);
I believe you need to use addEventListener to attach the callback to a web component:
if (ref.current) ref.current.addEventListener('change', onChange);

How to change a component's state correctly from another component as a login method executes?

I have two components - a sign in form component that holds the form and handles login logic, and a progress bar similar to the one on top here in SO. I want to be able to show my progress bar fill up as the login logic executes if that makes sense, so as something is happening show the user an indication of loading. I've got the styling sorted I just need to understand how to correctly trigger the functions.
I'm new to React so my first thought was to define handleFillerStateMax() and handleFillerStateMin() within my ProgressBarComponent to perform the state changes. As the state changes it basically changes the width of the progress bar, it all works fine. But how do I call the functions from ProgressBarComponent as my Login component onSubmit logic executes? I've commented my ideas but they obviously don't work..
ProgressBarComponent:
class ProgressBarComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
percentage: 0
}
}
// the functions to change state
handleFillerStateMax = () => {
this.setState ({percentage: 100})
}
handleFillerStateMin = () => {
this.setState ({percentage: 0})
}
render () {
return (
<div>
<ProgressBar percentage={this.state.percentage}/>
</div>
)
}
}
Login component:
class SignInFormBase extends Component {
constructor(props) {
super(props);
this.state = {...INITIAL_STATE};
}
onSubmit = event => {
const {email, password} = this.state;
// ProgressBarComponent.handleFillerMax()????
this.props.firebase
.doSignInWithEmailAndPass(email,password)
.then(()=> {
this.setState({...INITIAL_STATE});
this.props.history.push('/');
//ProgressBarComponent.handleFillerMin()????
})
.catch(error => {
this.setState({error});
})
event.preventDefault();
}
Rephrase what you're doing. Not "setting the progress bar's progress" but "modifying the applications state such that the progress bar will re-render with new data".
Keep the current progress in the state of the parent of SignInFormBase and ProgressBarComponent, and pass it to ProgressBarComponent as a prop so it just renders what it is told. Unless there is some internal logic omitted from ProgressBar that handles its own progress update; is there?
Pass in a callback to SignInFormBase that it can call when it has new information to report: that is, replace ProgressBarComponent.handleFillerMax() with this.props.reportProgress(100) or some such thing. The callback should setState({progress: value}).
Now, when the SignInFormBase calls the reportProgress callback, it sets the state in the parent components. This state is passed in to ProgressBarComponent as a prop, so the fact that it changed will cause he progress bar to re-render.
Requested example for #2, something like the following untested code:
class App extends Component {
handleProgressUpdate(progress) {
this.setState({progress: progress});
}
render() {
return (
<MyRootElement>
<ProgressBar progress={this.state.progress} />
<LoginForm onProgressUpudate={(progress) => this.handleProgressUpdate(progress)} />
</MyRootElemen>
)
}
}
The simply call this.props.onProgressUpdate(value) from LoginForm whenever it has new information that should change the value.
In basic terms, this is the sort of structure to go for (using useState for brevity but it could of course be a class-based stateful component if you prefer):
const App = ()=> {
const [isLoggingIn, setIsLoggingIn] = useState(false)
const handleOnLoginStart = () => {
setIsLoggingIn(true)
}
const handleOnLoginSuccess = () => {
setIsLoggingIn(false)
}
<div>
<ProgressBar percentage={isLoggingIn?0:100}/>
<LoginForm onLoginStart={handleOnLogin} onLoginSuccess={handleOnLoginSuccess}/>
</div>
}
In your LoginForm you would have:
onSubmit = event => {
const {email, password} = this.state;
this.props.onLoginStart() // <-- call the callback
this.props.firebase
.doSignInWithEmailAndPass(email,password)
.then(()=> {
this.setState({...INITIAL_STATE});
this.props.history.push('/');
this.props.onLoginSuccess() // <-- call the callback
})
.catch(error => {
this.setState({error});
})
event.preventDefault();
}

React: Get state of children component component in parent

I have this container where and is not placed in the same level. How can I get the state of the Form when I click on the button (which is placed on the parent) ?
I've created a demo to address my issue.
https://codesandbox.io/s/kmqw47p8x7
class App extends React.Component {
constructor(props) {
super(props);
}
save = () => {
alert("how to get state of Form?");
//fire api call
};
render() {
return (
<div>
<Form />
<button onClick={this.save}>save</button>
</div>
);
}
}
One thing I don't want to do is sync the state for onChange event, because within Form there might be another Form.
To access a child instance from parent, your need to know about ref:
First, add formRef at top your App class:
formRef = React.createRef();
Then in App render, pass ref prop to your Form tag:
<Form ref={this.formRef} />
Finaly, get state from child form:
save = () => {
alert("how to get state of Form?");
const form = this.formRef.current;
console.log(form.state)
};
Checkout demo here
ideally, your form submit action belongs to the Form component
You can put button inside your From component and pass a submit callback to the form.
class App extends React.Component {
constructor(props) {
super(props);
}
save = (data) => {
// data is passed by Form component
alert("how to get state of Form?");
//fire api call
};
render() {
return (
<div>
<Form onFormSubmit={this.save} />
</div>
);
}
}
you can write the code like this
https://codesandbox.io/s/23o469kyx0
As it was mentioned, a ref can be used to get stateful component instance and access the state, but this breaks encapsulation:
<Form ref={this.formRef}/>
A more preferable way is to refactor Form to handle this case, i.e. accept onChange callback prop that would be triggered on form state changes:
<Form onChange={this.onFormChange}/>
One thing I don't want to do is sync the state for onChange event, because within Form there might be another Form.
Forms will need to handle this any way; it would be impossible to reach nested form with a ref from a grandparent. This could be the case for lifting the state up.
E.g. in parent component:
state = {
formState: {}
};
onFormChange = (formState) => {
this.setState(state => ({
formState: { ...state.formState, ...formState }
}));
}
render() {
return (
<Form state={this.state.formState} onChange={this.onFormChange} />
);
}
In form component:
handleChange = e =>
this.props.onChange({
[e.target.name]: e.target.value
});
render() {
return (
<input
onChange={this.handleChange}
name="firstName"
value={this.props.state.firstName}
/>
);
}
Here is a demo.

Is it ok to use a wrapper component to pass props in React?

export function injectProps() {
const injects = {store: new Store()}; // some store
return function (Component) {
return class Proxy extends React.Component {
render() {
return React.createElement(Component, {
...injects,
...this.props,
});
}
};
}
}
Is it ok to use this instead of Redux or Context API with React?
Update: I think I missed to point out my expectation. I'm actually passing some service(http, localStorage) to childrens only when they asks for it. It's not only about the store as services don't have any state. But I also need to pass store through it.
https://pastebin.com/G3PgVxLn
Maybe this tweet by the Dan Abramov (React maintainer) might help.
I understand it was probably not the point of the article. But I see
people reaching for Context or Redux because they don’t realize
components can take any children — and that often removes the need for
deep prop passing. Would be great to highlight!
And Dave Ceddia posted a relavant React documentation link.
Composition vs Inheritance
You can read upon those two.
And here is a demo Nicolas Marcora created to show me how to pass properties to child/children.
You can pass props to children using React.cloneElement(child,...
Working demo on StackBlitz.
export default class WithMouse extends React.Component {
state = { x: 0, y: 0 }
handleMouseMove = event => { ... }
render() {
const { children } = this.props
const childElements = React.Children.map(children, child =>
React.cloneElement(child, {
mouse: this.state,
onMouseMove: this.handleMouseMove
})
)
return <div>
{ childElements }
</div>
}
}
You can use WithMouse class to pass props downward to all children and use it like following.
class App extends Component {
...
render() {
return (
<WithMouse>
<MouseTracker />
</WithMouse>
);
}
}
MouseTracker has access to props passed from WithMouse so you can just use it without directly passing it manually.
You can probably go further and pass all props instead of a few (mouse, onMouseMove)

Resources