How to access a child's state in React - javascript

I have the following structure:
FormEditor - holds multiple instances of FieldEditor
FieldEditor - edits a field of the form and saving various values about it in its state
When a button is clicked within FormEditor, I want to be able to collect information about the fields from all FieldEditor components, information that's in their state, and have it all within FormEditor.
I considered storing the information about the fields outside of FieldEditor's state and put it in FormEditor's state instead. However, that would require FormEditor to listen to each of its FieldEditor components as they change and store their information in its state.
Can't I just access the children's state instead? Is it ideal?

Just before I go into detail about how you can access the state of a child component, please make sure to read Markus-ipse's answer regarding a better solution to handle this particular scenario.
If you do indeed wish to access the state of a component's children, you can assign a property called ref to each child. There are now two ways to implement references: Using React.createRef() and callback refs.
Using React.createRef()
This is currently the recommended way to use references as of React 16.3 (See the documentation for more information). If you're using an earlier version then see below regarding callback references.
You'll need to create a new reference in the constructor of your parent component and then assign it to a child via the ref attribute.
class FormEditor extends React.Component {
constructor(props) {
super(props);
this.FieldEditor1 = React.createRef();
}
render() {
return <FieldEditor ref={this.FieldEditor1} />;
}
}
In order to access this kind of ref, you'll need to use:
const currentFieldEditor1 = this.FieldEditor1.current;
This will return an instance of the mounted component so you can then use currentFieldEditor1.state to access the state.
Just a quick note to say that if you use these references on a DOM node instead of a component (e.g. <div ref={this.divRef} />) then this.divRef.current will return the underlying DOM element instead of a component instance.
Callback Refs
This property takes a callback function that is passed a reference to the attached component. This callback is executed immediately after the component is mounted or unmounted.
For example:
<FieldEditor
ref={(fieldEditor1) => {this.fieldEditor1 = fieldEditor1;}
{...props}
/>
In these examples the reference is stored on the parent component. To call this component in your code, you can use:
this.fieldEditor1
and then use this.fieldEditor1.state to get the state.
One thing to note, make sure your child component has rendered before you try to access it ^_^
As above, if you use these references on a DOM node instead of a component (e.g. <div ref={(divRef) => {this.myDiv = divRef;}} />) then this.divRef will return the underlying DOM element instead of a component instance.
Further Information
If you want to read more about React's ref property, check out this page from Facebook.
Make sure you read the "Don't Overuse Refs" section that says that you shouldn't use the child's state to "make things happen".

If you already have an onChange handler for the individual FieldEditors I don't see why you couldn't just move the state up to the FormEditor component and just pass down a callback from there to the FieldEditors that will update the parent state. That seems like a more React-y way to do it, to me.
Something along the line of this perhaps:
const FieldEditor = ({ value, onChange, id }) => {
const handleChange = event => {
const text = event.target.value;
onChange(id, text);
};
return (
<div className="field-editor">
<input onChange={handleChange} value={value} />
</div>
);
};
const FormEditor = props => {
const [values, setValues] = useState({});
const handleFieldChange = (fieldId, value) => {
setValues({ ...values, [fieldId]: value });
};
const fields = props.fields.map(field => (
<FieldEditor
key={field}
id={field}
onChange={handleFieldChange}
value={values[field]}
/>
));
return (
<div>
{fields}
<pre>{JSON.stringify(values, null, 2)}</pre>
</div>
);
};
// To add the ability to dynamically add/remove fields, keep the list in state
const App = () => {
const fields = ["field1", "field2", "anotherField"];
return <FormEditor fields={fields} />;
};
Original - pre-hooks version:
class FieldEditor extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const text = event.target.value;
this.props.onChange(this.props.id, text);
}
render() {
return (
<div className="field-editor">
<input onChange={this.handleChange} value={this.props.value} />
</div>
);
}
}
class FormEditor extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleFieldChange = this.handleFieldChange.bind(this);
}
handleFieldChange(fieldId, value) {
this.setState({ [fieldId]: value });
}
render() {
const fields = this.props.fields.map(field => (
<FieldEditor
key={field}
id={field}
onChange={this.handleFieldChange}
value={this.state[field]}
/>
));
return (
<div>
{fields}
<div>{JSON.stringify(this.state)}</div>
</div>
);
}
}
// Convert to a class component and add the ability to dynamically add/remove fields by having it in state
const App = () => {
const fields = ["field1", "field2", "anotherField"];
return <FormEditor fields={fields} />;
};
ReactDOM.render(<App />, document.body);

As the previous answers said, try to move the state to a top component and modify the state through callbacks passed to its children.
In case that you really need to access to a child state that is declared as a functional component (hooks) you can declare a ref in the parent component, and then pass it as a ref attribute to the child, but you need to use React.forwardRef and then the hook useImperativeHandle to declare a function you can call in the parent component.
Take a look at the following example:
const Parent = () => {
const myRef = useRef();
return <Child ref={myRef} />;
}
const Child = React.forwardRef((props, ref) => {
const [myState, setMyState] = useState('This is my state!');
useImperativeHandle(ref, () => ({getMyState: () => {return myState}}), [myState]);
})
Then you should be able to get myState in the Parent component by calling:
myRef.current.getMyState();

It's 2020 and lots of you will come here looking for a similar solution but with Hooks (they are great!) and with the latest approaches in terms of code cleanliness and syntax.
So as previous answers had stated, the best approach to this kind of problem is to hold the state outside of child component fieldEditor. You could do that in multiple ways.
The most "complex" is with a global context (state) that both parent and children could access and modify. It's a great solution when components are very deep in the tree hierarchy and so it's costly to send props in each level.
In this case I think it's not worth it, and a more simple approach will bring us the results we want, just using the powerful React.useState().
An approach with a React.useState() hook - way simpler than with Class components
As said, we will deal with changes and store the data of our child component fieldEditor in our parent fieldForm. To do that we will send a reference to the function that will deal and apply the changes to the fieldForm state, you could do that with:
function FieldForm({ fields }) {
const [fieldsValues, setFieldsValues] = React.useState({});
const handleChange = (event, fieldId) => {
let newFields = { ...fieldsValues };
newFields[fieldId] = event.target.value;
setFieldsValues(newFields);
};
return (
<div>
{fields.map(field => (
<FieldEditor
key={field}
id={field}
handleChange={handleChange}
value={fieldsValues[field]}
/>
))}
<div>{JSON.stringify(fieldsValues)}</div>
</div>
);
}
Note that React.useState({}) will return an array with position 0 being the value specified on call (Empty object in this case), and position 1 being the reference to the function
that modifies the value.
Now with the child component, FieldEditor, you don't even need to create a function with a return statement. A lean constant with an arrow function will do!
const FieldEditor = ({ id, value, handleChange }) => (
<div className="field-editor">
<input onChange={event => handleChange(event, id)} value={value} />
</div>
);
Aaaaand we are done, nothing more. With just these two slim functional components we have our end goal "access" our child FieldEditor value and show it off in our parent.
You could check the accepted answer from 5 years ago and see how Hooks made React code leaner (by a lot!).
Hope my answer helps you learn and understand more about Hooks, and if you want to check a working example here it is.

Now you can access the InputField's state which is the child of FormEditor.
Basically, whenever there is a change in the state of the input field (child), we are getting the value from the event object and then passing this value to the Parent where in the state in the Parent is set.
On a button click, we are just printing the state of the input fields.
The key point here is that we are using the props to get the input field's id/value and also to call the functions which are set as attributes on the input field while we generate the reusable child input fields.
class InputField extends React.Component{
handleChange = (event)=> {
const val = event.target.value;
this.props.onChange(this.props.id , val);
}
render() {
return(
<div>
<input type="text" onChange={this.handleChange} value={this.props.value}/>
<br/><br/>
</div>
);
}
}
class FormEditorParent extends React.Component {
state = {};
handleFieldChange = (inputFieldId , inputFieldValue) => {
this.setState({[inputFieldId]:inputFieldValue});
}
// On a button click, simply get the state of the input field
handleClick = ()=>{
console.log(JSON.stringify(this.state));
}
render() {
const fields = this.props.fields.map(field => (
<InputField
key={field}
id={field}
onChange={this.handleFieldChange}
value={this.state[field]}
/>
));
return (
<div>
<div>
<button onClick={this.handleClick}>Click Me</button>
</div>
<div>
{fields}
</div>
</div>
);
}
}
const App = () => {
const fields = ["field1", "field2", "anotherField"];
return <FormEditorParent fields={fields} />;
};
ReactDOM.render(<App/>, mountNode);

You may access the child state by passing a callback to the child component.
const Parent = () => {
return (
<Child onSubmit={(arg) => {
console.log('accessing child state from parent callback: ', arg)
}}
/>
)
}
const Child = ({onSubmit}) => {
const [text, setText] = useState('');
return (
<>
<input value={text} onChange={setText}>
<button onClick={() => onSubmit(text)} />
</>
)
}
Now if you click the button in the child component, you will execute the function passed from the parent and have access to the child component's state variables.

Related

Can we pass props from Parent to Child component in React, and set the props as state of child component?

const Parent=()=>{
return(
)
}
const Child=({data})=>{
return (
{data}
)
}
Can I set the props in Child component to its state?
Yes you can. Something like:
class Child extends Component {
constructor(props){
const {myProp} = props;
this.state = {myNumber: myProp}
render(){
const {myNumber} = this.state;
return <div>{myNumber}</div>
}
class Parent extends Component {
render () {
return <Child myProp={5} />
}
}
BUT: if your "parent" component refreshes, so does the child and the state is reverted back to the value set by the parent, and all the state changes you did on the child are lost.
Yeah here made a code sandbox example showing how to do it: https://codesandbox.io/s/stack-pass-props-fgq67n
Child component looks like this
const Input = ({ string }) => {
const [value, setValue] = useState(string);
return <input value={value} onChange={(e) => setValue(e.target.value)} />;
};
Parent Component:
export default function App() {
return (
<div className="App">
<Input string="default" />
</div>
);
}
Sometimes if the parent reloads the passed props may reset to defaults
There is a concept in React known as controlled and uncontrolled components, When your component only have props and no state inside itself then it a controlled component, that is controlled by the parent component.
Also, it is not advisable to convert the props to the state of the child component, if you want to change the value of the props, just pass an additional method as a props which will be called by the Child to update the value of the props, let show you with an example
const Parent = () => {
const [number, setNumber] = useState(0);
const updateNumberHandler = (numberToUpdate) => setNumber(numberToUpdate)
return <Child number={number} onUpdateNumber={updateNumberHandler} />
}
const Child = (props) => {
const { number, onUpdateNumber } = props;
return <button onClick={() => onUpdateNumber(number + 1)}>Updated Number: {number}</button>
}
Here you can see I am passing two props one value and one method to update that value, when the onUpdateNumber method is called the value on the parent is updated so it gets re-render and also the child gets re-render with the updated number value.

how to pass an event from a child component to parent component back down to another child in React

Lets say for instance that I have three components in React, an App (the parent component), a button component and a component that is meant to display something, can be anything doesn't really matter. Lets say in the button component is activated, how would I pass the information (ie that the event actually happened) to the App parent component back down to the other child component to let it know a specific event happened to display some message?
this is how I would go about dong this using hooks :
const Parent=(props)=>{
[eventHappend,setEventHappend]=useState(false)
return (
<div>
<Child1 setEventHappend={setEventHappend} />
<Child2 eventHappend={eventHappend} />
</div>
)
}
const Child =({setEventHappend})=>{
return (
<div>
<button onClick={e=>setEventHappend(true)} > click me 1 </button>
</div>
)
}
const Child2 =({eventHappend})=>{
return (
<div>
<button onClick={e=>{/* some code*/ }} > {eventHappend?'event happend':'event didnt happen yet '} </button>
</div>
)
}
There are various ways you can achieve this pass state as props to the child elements (must know before other methods), context or use redux which has a store.
Generally speaking. React has one way data flow, uni directional. As in the parent will hold the state and will be passed to child elements.
Here App holds the state buttonClick which has the information about the event.
const App = () => {
const [ buttonClick, setButtonClick] = React.useState(false);
const messageToBeDispalyed = "The button has been clicked"
return (
<div>
<CustomButton setEventHappened={setButtonClick} />
<DisplayText value = {buttonClick ? messageToBeDispalyed : ""} />
</div>
)
}
const CustomButton = (props) =>{
return <button onClick={(e)=>props.setEventHappened(true)} > Click Me </button>
}
const DisplayText = (props) => {
return <h1> {props.value} </h1>
}
Similar answers to the others, but you would pass down a method to the child from the parent to update the state. But be aware that by doing this will cause a rerender for all of the parent's children.
const Parent = () => {
const [state, setState] = React.useState(false);
const handleClick = value => {
setState(value);
};
return (
<Child state={state} handleClick={handleClick} />
<OtherChild isTrue={state} /> // this component needs data changed by <Child />
)
};
const Child = props => {
const {state, handleClick} = props;
return (
<button onClick={() => handleClick(!state)} >click me</button>
);
};
This way the parent alone handles the state change and provides that method to the child.
as #Loveen Dyall and #Shruti B mentioned you can use RXJS for a more modular approach ,While RxJS is typically thought of as being used with Angular projects, it's a completely separate library that can be used with other JavaScript frameworks like React and Vue.
When using RxJS with React, the way to communicate between components is to use an Observable and a Subject (which is a type of observable), I won't go too much into the details about how observables work here since it's a big subject, but in a nutshell there are two methods that we're interested in: Observable.subscribe() and Subject.next().
learn more about RXJS and Observables : https://blog.logrocket.com/understanding-rxjs-observables/
Observable.subscribe()
The observable subscribe method is used by React components to subscribe to messages that are sent to an observable.
Subject.next()
The subject next method is used to send messages to an observable which are then sent to all React components that are subscribers (a.k.a. observers) of that observable.
here is how you implement it in this use case :
this is called a service and you would put this file in a services folder
import { Subject } from 'rxjs';
const subject = new Subject();
//here where sending { event: eventTitle } , that way you can listen to diffrent events , for example 'INCREMENTED' you could even send values
export const eventsService= {
sendEvent: eventTitle => subject.next({ title: eventTitle }),
getEventNotification: () => subject.asObservable()
};
in your Child 1 component you would subscribe to the observable in useEffect or compoentDidMount if your using class component:
import { eventsService} from '../services';
const Child1 =()=>{
const [child2EventFired,setChild2EventFired]=useState(false)
useEffect(()=>{
let subscription = eventsService.getEventNotification().subscribe(eventTitle =>
{
if (eventTitle=="CHILD2_BUTTON_CLICK" ) {
setChild2EventFired(true)
}else{
setChild2EventFired(false)
}
});
return ()=>{
subscription.unsubscribe();
}
},[])
return <div>
<button> {child2EventFired? 'child2 button event fired':'event not fired yet'} </button>
</div>
}
in your Child 2 component
import { eventsService} from '../services';
const Child2 =()=>{
Child2Click=(e)=>{
//some code,
//then send messages to the observable observable
eventsService.sendEvent('CHILD2_BUTTON_CLICK');
}
return <div>
<button onClick={Child2Click} >click me</button>
</div>
}

How to find focused React component? (like document.activeElement)

If you had 500 components, each with a ref, how would you find which component has the user's focus? All components with a ref are focusable elements like <input />, <textarea />, etc. For simplicity, all of the refs are accessible from a single top-level component.
This is very simple if your React components have classNames - but if you want to find the ref of a document.activeElement, if there some way to achieve that without having to resort to classNames?
To touch on why we don't have classNames, we're using JSS via emotion. To have to manually assign everything a className just for this purpose would be rather absurd. No obvious alternative has occurred to me.
This may be a good use case for a custom hook that hooks into the native DOM methods to track focus events and then returns the active element. This will log the active element every time a new element receives focus:
const useActiveElement = () => {
const [active, setActive] = useState(document.activeElement);
const handleFocusIn = (e) => {
setActive(document.activeElement);
}
useEffect(() => {
document.addEventListener('focusin', handleFocusIn)
return () => {
document.removeEventListener('focusin', handleFocusIn)
};
}, [])
return active;
}
const App = () => {
const focusedElement = useActiveElement();
useEffect(() => {
if (focusedElement) {
focusedElement.value && console.log(focusedElement.value);
}
console.log(focusedElement);
}, [focusedElement])
return (
<div>
<input type="text"/>
<button>Button</button>
</div>
)
}
However, correlating this element with your refs could prove tricky, as you'd need to keep a collection of the refs to search through which would probably involve giving each element its own callback ref to store in an Array or something similar. But depending on what you need to do with the element once it's focused, it may not be necessary. For example, the code I posted above will log the value of the input if it exists. It would help to know more specifically what your use case is for tracking this data.
Do you want something like that, react gives you an option to track/focus using ref
import React, { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
// create a ref to store the textInput DOM element
this.textInput = React.createRef();
this.button = React.createRef();
this.textarea = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// this.textInput.current.focus();
this.button.current.focus();
// this.textarea.current.focus();
}
render() {
return (
<div>
<input type="text" ref={this.textInput} />
<button ref={this.button}>something</button>
<textarea ref={this.textarea}></textarea>
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
export default App;
You can get a detail here

Get containing component from nested component

I am writing a ControlledInput component, and in order to have access to the state of the component using ControlledInput, I have a binder prop in ControlledInput.
I'm having a slight issue when using the component:
render() {
const CI = props => <ControlledInput binder={this} {...props} />;
return (
<div style={styles.container}>
<h1>NEW RECIPE</h1>
<ControlledInput binder={this} label={"Title"} />
</div>
);
}
The implementation above works completely fine. However, note the const CI I've defined. I tried to use this so I could just write <CI label={"Title"}/> without the binder since the binder will be the same on all the ControlledInput components I use in a given render method.
The problem with using <CI label={"Title"}/> is that when I type into the input, the input "blurs" and I have to reselect it. This appears to be because the render method creates the CI on every render.
I hope I've explained that clearly, because my head hurts.
Anyway, it makes sense to me why this happens. And I know that one solution is to put const CI = props => <ControlledInput binder={this} {...props} />; outside of the render function. But then I'd have to call it as <this.CI> and that starts to defeat the purpose.
And I can't put CI in global scope because then I don't have access to this.
Is there a way to solve this?
Update
Here is the current (very much in progress) code for ControlledInput:
// #flow
import React, { Component } from "react";
type Props = {
containerStyle?: Object,
label: string,
propName?: string,
binder: Component<Object, Object>,
onChange?: Object => void
};
class ControlledInput extends Component<Props> {
render() {
const props = this.props;
const propName = props.propName || props.label.toLowerCase();
return (
<div style={props.containerStyle}>
<p>{props.label}</p>
<input
type="text"
label={props.label}
onChange={
this.props.onChange ||
(e => {
props.binder.setState({ [propName]: e.target.value });
})
}
value={props.binder.state[propName]}
></input>
</div>
);
}
}
The point of this whole endeavor is to simplify creating a form with controlled components, avoiding having to add value={this.state.whatever} and onChange={e=>this.setState({whatever: e})} to each one, which is not DRY in my opinion.
And then I want get a little more DRY by not passing binder={this} to every component and that's why I'm doing const CI = props => <ControlledInput binder={this} {...props} />;, which, again, has to be inside the class to access this and inside the render function to be called as CI rather than this.CI.
So that first explanation why you need to pass this, although I suppose I could also have props like setState={this.setState} parentState={this.state}, and in that case it does indeed start to make sense to combine those into something like {...propsToSend} as #John Ruddell suggested.
Note that I've provided a possibility to override onChange, and plan on doing so for most or all of the other props (e.g, value={this.props.value || binder.state[propName]}. If one were to override a lot of these (especially value and onChange) it would indeed make the component much less reusable, but the main use case is for quickly creating multiple inputs that don't have special input handling.
So, again, my ideal would be to call <ControlledInput label="Title"/> and have the component code take care of binding state and setState correctly. If this is possible. And then the second option would be to have a place to define the necessary context props in a place that makes it simple when it's time to actually use the component multiple times, like so:
<ControlledInput label={"title"} {...contextProps}/>
<ControlledInput label={"author"} {...contextProps}/>
<ControlledInput label={"email"} {...contextProps}/>
<ControlledInput label={"content"} textArea={true} {...contextProps}/> // textarea prop not implemented yet, fyi
etc
I hear that accessing the parent state/context may be an anti-pattern, but there must be some way to do what I'm trying to do without using an anti-pattern, isn't there?
If you want the state of the parent, handle the state there and pass down the value to your input - ControlledInput won't have to know anything except how to handle data in and out. Something like this, and note that I jacked up the names a little so you can see which component is handling what:
import React, { useState } from "react"
const Parent = () => {
const [title, setTitle] = useState("")
const handleChangeInParent = (newTitle) => {
setTitle((oldValue) => newTitle)
}
return(<div style={styles.container}>
<h1>NEW RECIPE</h1>
<ControlledInput handleChange={handleChangeInParent} label={title} />
</div>)
}
const ControlledInput = ({handleChange, label}) => {
return (
<input onChange={handleChange} type="text" value={label} />
)
}
If ControlledComponent needs to handle its own state, then pass it a default value and then have the Parent read the value when saving (or whatever):
import React, { useState } from "react"
const Parent = () => {
const handleSaveInParent = (newTitle) => {
console.log("got the new title!")
}
return (
<div style={styles.container}>
<h1>NEW RECIPE</h1>
<ControlledInput handleSave={handleSaveInParent} initialLabel="Title" />
</div>
)
}
const ControlledInput = ({ handleSave, initialLabel }) => {
const [title, setTitle] = useState(initialLabel)
const handleChange = (ev) => {
const value = ev.target.value
setTitle((oldValue) => value)
}
const handleSubmit = (ev) => {
ev.preventDefault()
handleSave(title)
}
return (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} type="text" value={title} />
</form>
)
}
You shouldn't be sending this through - just send values and/or functions to handle values.
With Updated Implementation
(okay, John you win!)
Not positive if this is technically an "answer", but I've rewritten the component to take a state and (updated) a setterFn prop:
component
// #flow
import React, { Component } from "react";
type Props = {
containerStyle?: Object,
labelStyle?: Object,
label: string,
propName?: string,
state: Object,
onChange?: Object => void,
textArea?: boolean,
setterFn: (key: string, value: mixed) => void
};
class ControlledInput extends Component<Props> {
render() {
const props = this.props;
const propertyName = props.propName || props.label.toLowerCase();
const TagType = props.textArea ? "textarea" : "input";
// only pass valid props to DOM element (remove any problematic custom props)
const { setterFn, propName, textArea, ...domProps } = props;
return (
<div style={props.containerStyle}>
<p style={props.labelStyle}>{props.label}</p>
<TagType
{...domProps}
label={props.label} // actually could get passed automatically, but it's important so I'm leaving it in the code
onChange={
this.props.onChange ||
(setterFn ? e => setterFn(propertyName, e.target.value) : null)
}
value={props.state[propertyName] || ""}
></TagType>
</div>
);
}
}
export default ControlledInput;
in use (somehow less code than before!)
class Wrapper extends Component<Object, Object> {
state = {};
render() {
const setterFn = (k, v) => this.setState({ [k]: v });
const p = { state: this.state, setterFn: setterFn.bind(this) };
return <ControlledInput {...p} {...this.props.inputProps} />
}
}
I guess this is more appropriate. It still takes up a lot more space than binder={this}.
It doesn't actually the questions of:
How to access the parent's state from the component. Though from comments it seems like this is an anti-pattern, which I do understand from the theory of React.
How to set these repeating props elsewhere so that I can just call `. I guess the only solution is to do something like this:
render() {
const props = {state: this.state, setState: this.setState}
<ControlledInput {...props} label="Title"/>
}
Which certainly isn't such a bad solution. Especially if I shorten that name to, say, a single character.
Much thanks to #John Ruddell for setting me on the right path.

How to access a DOM element in React? What is the equilvalent of document.getElementById() in React

How do I select certain bars in react.js?
This is my code:
var Progressbar = React.createClass({
getInitialState: function () {
return { completed: this.props.completed };
},
addPrecent: function (value) {
this.props.completed += value;
this.setState({ completed: this.props.completed });
},
render: function () {
var completed = this.props.completed;
if (completed < 0) { completed = 0 };
return (...);
}
I want to use this React component:
var App = React.createClass({
getInitialState: function () {
return { baction: 'Progress1' };
},
handleChange: function (e) {
var value = e.target.value;
console.log(value);
this.setState({ baction: value });
},
handleClick10: function (e) {
console.log('You clicked: ', this.state.baction);
document.getElementById(this.state.baction).addPrecent(10);
},
render: function () {
return (
<div class="center">Progress Bars Demo
<Progressbar completed={25} id="Progress1" />
<h2 class="center"></h2>
<Progressbar completed={50} id="Progress2" />
<h2 class="center"></h2>
<Progressbar completed={75} id="Progress3" />
<h2 class="center"></h2>
<span>
<select name='selectbar' id='selectbar' value={this.state.baction} onChange={this.handleChange}>
<option value="Progress1">#Progress1</option>
<option value="Progress2">#Progress2</option>
<option value="Progress3">#Progress3</option>
</select>
<input type="button" onClick={this.handleClick10} value="+10" />
<button>+25</button>
<button>-10</button>
<button>-25</button>
</span>
</div>
)
}
});
I want to execute the handleClick10 function and perform the operation for my selected progressbar.
But the result I get is:
You clicked: Progress1
TypeError: document.getElementById(...) is null
How do I select the certain Element in react.js?
You can do that by specifying the ref
EDIT: In react v16.8.0 with function component, you can define a ref with useRef. Note that when you specify a ref on a function component, you need to use React.forwardRef on it to forward the ref to the DOM element of use useImperativeHandle to to expose certain functions from within the function component
Ex:
const Child1 = React.forwardRef((props, ref) => {
return <div ref={ref}>Child1</div>
});
const Child2 = React.forwardRef((props, ref) => {
const handleClick= () =>{};
useImperativeHandle(ref,() => ({
handleClick
}))
return <div>Child2</div>
});
const App = () => {
const child1 = useRef(null);
const child2 = useRef(null);
return (
<>
<Child1 ref={child1} />
<Child1 ref={child1} />
</>
)
}
EDIT:
In React 16.3+, use React.createRef() to create your ref:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
In order to access the element, use:
const node = this.myRef.current;
DOC for using React.createRef()
EDIT
However facebook advises against it because string refs have some issues, are considered legacy, and are likely to be removed in one of the future releases.
From the docs:
Legacy API: String Refs
If you worked with React before, you might be
familiar with an older API where the ref attribute is a string, like
"textInput", and the DOM node is accessed as this.refs.textInput. We
advise against it because string refs have some issues, are considered
legacy, and are likely to be removed in one of the future releases. If
you're currently using this.refs.textInput to access refs, we
recommend the callback pattern instead.
A recommended way for React 16.2 and earlier is to use the callback pattern:
<Progressbar completed={25} id="Progress1" ref={(input) => {this.Progress[0] = input }}/>
<h2 class="center"></h2>
<Progressbar completed={50} id="Progress2" ref={(input) => {this.Progress[1] = input }}/>
<h2 class="center"></h2>
<Progressbar completed={75} id="Progress3" ref={(input) => {this.Progress[2] = input }}/>
DOC for using callback
Even older versions of react defined refs using string like below
<Progressbar completed={25} id="Progress1" ref="Progress1"/>
<h2 class="center"></h2>
<Progressbar completed={50} id="Progress2" ref="Progress2"/>
<h2 class="center"></h2>
<Progressbar completed={75} id="Progress3" ref="Progress3"/>
In order to get the element just do
var object = this.refs.Progress1;
Remember to use this inside an arrow function block like:
print = () => {
var object = this.refs.Progress1;
}
and so on...
For getting the element in react you need to use ref and inside the function you can use the ReactDOM.findDOMNode method.
But what I like to do more is to call the ref right inside the event
<input type="text" ref={ref => this.myTextInput = ref} />
This is some good link to help you figure out.
With newer versions of React you can use and manipulate the DOM via hooks like this:
import React, { useEffect, useRef } from "react";
const MyComponent = () => {
const myContainer = useRef(null);
useEffect(() => {
console.log("myContainer..", myContainer.current);
});
return (
<>
<h1>Ref with react</h1>
<div ref={myContainer}>I can use the DOM with react ref</div>
</>
);
};
export default MyComponent;
Whenever you want to access your DOM element just use myContainer.current
You can replace
document.getElementById(this.state.baction).addPrecent(10);
with
this.refs[this.state.baction].addPrecent(10);
<Progressbar completed={25} ref="Progress1" id="Progress1"/>
Disclaimer: While the top answer is probably a better solution, as a beginner it's a lot to take in when all you want is something very simple. This is intended as a more direct answer to your original question "How can I select certain elements in React"
I think the confusion in your question is because you have React components which you are being passed the id "Progress1", "Progress2" etc. I believe this is not setting the html attribute 'id', but the React component property. e.g.
class ProgressBar extends React.Component {
constructor(props) {
super(props)
this.state = {
id: this.props.id <--- ID set from <ProgressBar id="Progress1"/>
}
}
}
As mentioned in some of the answers above you absolutely can use document.querySelector inside of your React app, but you have to be clear that it is selecting the html output of your components' render methods. So assuming your render output looks like this:
render () {
const id = this.state.id
return (<div id={"progress-bar-" + id}></div>)
}
Then you can elsewhere do a normal javascript querySelector call like this:
let element = document.querySelector('#progress-bar-Progress1')
You have to follow two different ways to do it in Class and Functional components.
For class components
<input type="text" ref={ref => this.myTextInput = ref} />
Look at the above code. Use "ref" attribute to refer to the relevant element. Then you will be able to refer to that element using that reference. In this example, I can use "this.myTextInput" to refer to the above input element.
For functional components
const textInput = useRef(null)
Use the "useRef" hook and set that variable name as the value of the "ref" attribute of the element you want to refer to (like below).
<input type="text" ref={textInput} />
An example for this on functional components.
import React, {useRef} from 'react'
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} />
</div>
);
}
Want to learn more? Here you go
Since React uses JSX code to create an HTML we cannot refer dom using regulation methods like documment.querySelector or getElementById.
Instead we can use React ref system to access and manipulate Dom as shown in below example:
constructor(props){
super(props);
this.imageRef = React.createRef(); // create react ref
}
componentDidMount(){
**console.log(this.imageRef)** // acessing the attributes of img tag when dom loads
}
render = (props) => {
const {urls,description} = this.props.image;
return (
<img
**ref = {this.imageRef} // assign the ref of img tag here**
src = {urls.regular}
alt = {description}
/>
);
}
}
In my case, I wasn't able to use ref because elements were somewhere between many child components and I have to access them by class and id instead of ref. So, trying with useEffect hook didn't work as it can't find the element:
useEffect(() => {
const el1 = document.querySelector('.el1')
const el2 = document.querySelector('.el2')
}, [])
The element is undefined because when it is mounted the children components also doesn't mounted before this parent component.
So, what I did is to use timeout:
useEffect(() => {
const timer = setTimeout(() => {
const el1 = document.querySelector('.el1')
const el2 = document.querySelector('.el2')
},500)
return () => {
clearTimeout(timer)
}
}, [])
Now, it worked fine. It found the DOM and I was able to manipulate with them. Hope, this helps someone!
The equivalent of document.getElementById() in React is document.querySelector().

Categories

Resources