I have a children component as a shared form using with different parent components, those parent components have different buttons and their actions.
My parent component is like:
const form = useForm({
defaultValues: {
name: '',
}
})
const { handleSubmit } = form
const onSubmit = (data) => { ... }
return (
<>
<Child form={form} />
<button onClick={handleSubmit(onsubmit)} />
</>
)
The child component is:
const {register, control } = props.form
return (
<form>
<Controller control={control} name="name" render={(field, fieldState) => <input .../>} />
</form>
)
How can I test the children component input behavior?
I used a jest.mock to react-hook-form:
jest.mock('react-hook-form', () => {
...jest.requireActural('react-hook-form')
Controller: ()=><></>
useForm:()=>({
Control: ()=>({}),
handleSubmit: ()=>jest.fn()
})
})
But this mock cannot get the input on the DOM. On the screen.debug, I can only see a tag renders, but not the content.
https://codesandbox.io/s/react-hook-form-v7-controller-forked-fwjqxg?file=/src/index.js
I am attempting to use the react-select "Select" component in my app as a convenient way to generate a series of drop-down lists as part of my UI.
From the react-select documentation, I have been able to get these components to render with the desired options; However, the value stored from the Select components is stored as a state variable via the useState hook.
I need to pass the value (state) of these menus to parent components. I am at a loss of how to get the state value generated in these functions to be able to be read in higher parent components. Below are some snippets of what I currently have.
NOTE: the arrays which populate the options props for these child functions are generated at a higher parent component and passed down as props. There seems to be no issue with a portion of the code
function ButtonA(props){
const options=props.buttonAOptions;
const [selectedOption, setSelectedOption]=useState(null);
return(
<div>
<p>Button A
<Select
value={selectedOption}
onChange={newValue =>setSelectedOption(newValue)}
options={options}
/>
</p>
</div>
)
}
function ButtonB(props){
const options=props.buttonBOptions;
const [selectedOption, setSelectedOption]=useState(null);
return(
<div>
<p>Button B
<Select
value={selectedOption}
onChange={newValue =>setSelectedOption(newValue)}
options={options}
/>
</p>
</div>
)
}
class Window extends React.Component {
constructor(props){
super(props);
}
// there are some other methods in here that are not related to this problem
render(){
return (
<div>
<ButtonA
buttonAOptions={this.props.buttonAOptions}
/>
<ButtonB
buttonBOptions={this.props.buttonBOption}
/>
</div>
)
}
}
You can communicate to a parent component through callbacks. You can lift the state and pass a callback to change the state on the parent, that is also passed as props to your component, or you can maintain the same state on both parent and child and just communicate changes from the children to the parent.
Example:
function Parent() {
const [counters, setCounters] = useState({ a: 0, b: 0 });
return (
<>
A: {counters.a}
<Child onChange={(value) => setCounters((s) => ({ ...s, a: value }))} />
B: {counters.b}
<Child onChange={(value) => setCounters((s) => ({ ...s, b: value }))} />
</>
)
}
function Child({ onChange }) {
const [counter, setCounter] = useState(0);
function handleClick() {
setCounter((s) => {
const newValue = s + 1;
onChange && onChange(newValue);
return newValue;
})
}
return <button onClick={handleClick}>Add 1</button>;
}
This code example is minimal representation of what the structure is
Main component where we group all other components:
<Form>
<FormGroup>
<RadioGroup>
<RadioButton/>
<RadioButton/>
</RadioGroup>
</FormGroup>
<FormGroup>
<TextInput />
</FormGroup>
<Button>Submit Form</Button>
</Form>
The goal is to create a reference to every TextInput in the FormGroup or to every RadioButton in RadioGroup or even FormGroup, so lets go further down the components, for now Form and FormGroup are empty components rendering children:
const Form: React.FunctionComponent<Props> = ({ children }) => {
return (
<form>
{children}
</form>
);
};
const FormGroup: React.FunctionComponent<Props> = ({ children }) => {
// WE WANT TO ACCESS REF HERE, with React.Children.map every child's ref is always null
return (
{children}
);
};
To keep it simple the RadioGroup also just rendering children
const RadioGroup: React.FunctionComponent<Props> = ({ children }) => {
// WE WANT TO ACCESS REF HERE, with React.Children.map every child's ref is always null
return (
{children}
);
};
And we are getting to the main point, the Child we want to create a reference to, in this example the RadioButton component
class RadioButton extends Component<{props}, state> {
this.state = {
inputRef: React.createRef()
};
handleClick() {
WE CAN ACCESS THE REF HERE
// this.state.inputRef.current
}
render() {
return (
<div> // putting the ref here also doesnt work in parent components
<label>
<input
ref={this.state.inputRef}
onChange={() => this.handleClick()}
/>
</label>
</div>
);
}
};
I would suggest you to use react-hook-form if you are working with form.
react-hook-form
how to get the render props from a component and use it in useEffect. In order to make an api call that prop is required .
So what would be the best way to handle this scenario?
const test = () => {
useEffect(() => {
prop1("api call url and input to be given here" )
},[dependencies])
return (
<div>
<input ></input>
<input></input>
<component>
{({ prop1, prop2, prop3 }) => {
}}
</component>
</div>
);
};
export default test;
add props as the parameter in the test const
to or make distracter fro the props as {}
const test = (props) => {
//what should be do
}
to use props used props. attribute
I am looking to create a stateless component who's input element can be validated by the parent component.
In my example below, I am running into a problem where the input ref is never being assigned to the parent's private _emailAddress property.
When handleSubmit is called, this._emailAddress is undefined. Is there something I'm missing, or is there a better way to do this?
interface FormTestState {
errors: string;
}
class FormTest extends React.Component<void, FormTestState> {
componentWillMount() {
this.setState({ errors: '' });
}
render(): JSX.Element {
return (
<main role='main' className='about_us'>
<form onSubmit={this._handleSubmit.bind(this)}>
<TextInput
label='email'
inputName='txtInput'
ariaLabel='email'
validation={this.state.errors}
ref={r => this._emailAddress = r}
/>
<button type='submit'>submit</button>
</form>
</main>
);
}
private _emailAddress: HTMLInputElement;
private _handleSubmit(event: Event): void {
event.preventDefault();
// this._emailAddress is undefined
if (!Validators.isEmail(this._emailAddress.value)) {
this.setState({ errors: 'Please enter an email address.' });
} else {
this.setState({ errors: 'All Good.' });
}
}
}
const TextInput = ({ label, inputName, ariaLabel, validation, ref }: { label: string; inputName: string; ariaLabel: string; validation?: string; ref: (ref: HTMLInputElement) => void }) => (
<div>
<label htmlFor='txt_register_first_name'>
{ label }
</label>
<input type='text' id={inputName} name={inputName} className='input ' aria-label={ariaLabel} ref={ref} />
<div className='input_validation'>
<span>{validation}</span>
</div>
</div>
);
You can useuseRef hook which is available since v16.7.0-alpha.
EDIT: You're encouraged to use Hooks in production as of 16.8.0 release!
Hooks enable you to maintain state and handle side effects in functional components.
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
Read more in Hooks API documentation
EDIT: You now can with React Hooks. See the answer by Ante Gulin.
You can't access React like methods (like componentDidMount, componentWillReceiveProps, etc) on stateless components, including refs. Checkout this discussion on GH for the full convo.
The idea of stateless is that there isn't an instance created for it (state). As such, you can't attach a ref, since there's no state to attach the ref to.
Your best bet would be to pass in a callback for when the component changes and then assign that text to the parent's state.
Or, you can forego the stateless component altogether and use an normal class component.
From the docs...
You may not use the ref attribute on functional components because they don't have instances. You can, however, use the ref attribute inside the render function of a functional component.
function CustomTextInput(props) {
// textInput must be declared here so the ref callback can refer to it
let textInput = null;
function handleClick() {
textInput.focus();
}
return (
<div>
<input
type="text"
ref={(input) => { textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
This is late but I found this solution much better.
Pay attention to how it uses useRef & how properties are available under current property.
function CustomTextInput(props) {
// textInput must be declared here so the ref can refer to it
const textInput = useRef(null);
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
For more reference check react docs
The value of your TextInput is nothing more than a state of your component. So instead of fetching the current value with a reference (bad idea in general, as far as I know) you could fetch the current state.
In a reduced version (without typing):
class Form extends React.Component {
constructor() {
this.state = { _emailAddress: '' };
this.updateEmailAddress = this.updateEmailAddress.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
updateEmailAddress(e) {
this.setState({ _emailAddress: e.target.value });
}
handleSubmit() {
console.log(this.state._emailAddress);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
value={this.state._emailAddress}
onChange={this.updateEmailAddress}
/>
</form>
);
}
}
You can also get refs into functional components with a little plumbing
import React, { useEffect, useRef } from 'react';
// Main functional, complex component
const Canvas = (props) => {
const canvasRef = useRef(null);
// Canvas State
const [canvasState, setCanvasState] = useState({
stage: null,
layer: null,
context: null,
canvas: null,
image: null
});
useEffect(() => {
canvasRef.current = canvasState;
props.getRef(canvasRef);
}, [canvasState]);
// Initialize canvas
useEffect(() => {
setupCanvas();
}, []);
// ... I'm using this for a Konva canvas with external controls ...
return (<div>...</div>);
}
// Toolbar which can do things to the canvas
const Toolbar = (props) => {
console.log("Toolbar", props.canvasRef)
// ...
}
// Parent which collects the ref from Canvas and passes to Toolbar
const CanvasView = (props) => {
const canvasRef = useRef(null);
return (
<Toolbar canvasRef={canvasRef} />
<Canvas getRef={ ref => canvasRef.current = ref.current } />
}