React native: invoke method dynamically - javascript

How to invoke dynamically named method in Javascript.
I'm using React Native and when assign ref in TextInput I need that set dynamically method.
renderInput(id) {
<TextInput
ref={(input) => this.myInput = input}
/>
}
I need dynamic this.myInput.
I've tested it and got it wrong: this.myInput[id] and this[id].myInput

You should be using current:
this.myInput.current[id]
Here's how:
// First, create a reference (in the constructor)
this.myInput = React.createRef()
// Then, assign the ref (in the element)
<TextInput ref={this.myInput} />
// Now, to access the reference
// this.myInput.current.<<property you wanted to access>>
this.myInput.current[id] // id is defined as a variable
// this will get the dom id
// this.myInput.current.id
But if you insist using callback ref like you're currently having, then you can pass the id prop: (Also, I think the answer you're looking for is)
renderInput(id) {
<TextInput
ref={(input) => this.myInput = input}
id={id} {/* you were missing this in fact */}
/>
}
Now, to get that id:
this.myInput.id // will work fine, coz we have id prop now
// gets DOM id that you assigned in your element

constructor(props) {
super(props);
this.inputs = {};
}
// params Object should be in this format {id:324, referenceKey: 'one'};
renderTextInput(params) {
return <TextInput ref={input => { this.inputs[params.referenceKey] = input }}/>
}
// Using the refernce
componentDidMount(){
this.inputs['one'].focus()
}

Related

How can i input string data in object?

I'm trying to input text to innerState.feedType. but when i tried
it says Cannot read property 'toString' of undefined
How can i fix my code?
this is my code
const [innerState, setInnerState] = useState<any>({
feedType: 'bbong',
})
const onChangeEtcfeedtype = useCallback((text) => {
setInnerState( innerState['feedType'] = text)
},[]);
<TextInput
placeholder="input."
value={innerState.feedType}
onChangeText={onChangeEtcfeedtype}
/>
You need to add null-safety(?) operator, because state creates after render components. Also need to return new link on object for re-render component.
const [innerState, setInnerState] = useState<any>({
feedType: 'bbong',
})
const onChangeEtcfeedtype = (text) => {
setInnerState({...innerState, innerState.feedType: text});
};
<TextInput
placeholder="input."
value={innerState?.feedType}
onChangeText={onChangeEtcfeedtype}
/>
I think this is how you should write it.
About the error. It look like its not come from this part. It come from your <TextInput /> component and possiblely caused because what you doing with onChange event.
const [innerState, setInnerState] = useState<any>({
feedType: 'bbong',
})
const onChangeEtcfeedtype = useCallback((text) => {
setInnerState({feedType: text})
},[]);
<TextInput
placeholder="input."
value={innerState.feedType}
onChangeText={onChangeEtcfeedtype}
/>
Please do consider read these article:
https://xyproblem.info/
https://stackoverflow.com/help/how-to-ask
Here, innerState['feedType'] = text you are changing the state directly and then store it using the updater function, you should not change it directly.
You have innerState which is object and it contains feedType property. So, update a state you have to pass the object with the same key with new value.
Correct Way:
setInnerState({feedType: text})

Material Ui Components props refer each other

How do I make material UI component property to carry the value of some other property of same component?
Here I want value property to refer checked property.
want to do something like this
<Switch
checked={singleHeading.required}
onChange={onHandleChangeCheck}
name="required"
value={event.target.checked}
/>
Using hooks you can perform that with this:
render() {
const [checked, setChecked] = useState(singleHeading.required);
const handleSwitchCheck = event => {
setChecked(event.target.checked);
onHandleChangeCheck(event);
};
// ... whatever code you have
<Switch
checked={checked}
onChange={handleSwitchCheck}
name="required"
value={checked}
/>
// ... whatever code you have
}

state not updating in child component - react js

I am passing a state from one component to another component. However, when a state updates in the parent component, the child component doesn't update. Is there something i am doing wrong in my code below .
As shown below, in the Patient.JS i pass the state to the AddPatient.JS. But when i the state of age is updated in the Patient.JS, it doesn't update in the AddPatient.JS.
How can i handle this ?
Patient.JS
state = {
info = {
age = '',
name = ''
}
}
handle_age(event)
{
this.setState({ age:event.target.value}, () => {
console.log('age', this.state.age)
<Modal>
<ModalHeader>
</ModalHeader>
<ModalBody>
<AddPatient
addNewPatient = {this.state.info}
/>
</ModalBody>
<ModalFooter>
<Button variant="contained" className="text-white" onClick={() => this.addPatient()}>Save</Button>
</ModalFooter>
</Modal>
AddPatient
const AddPatient = ({ addNewPatient}) => (
<FormGroup>
<Label for="className">Age</Label>
<Input
type="number"
name="sub_total"
id="sub_total"
placeholder=""
value = {addNewPatient.age}
/>
</FormGroup>
);
Replace your handle_age function with this:
handle_age(event)
{
let info = {...this.state.info}
info.age = event.target.value
this.setState({
info: {...info}
})
}
Explanation: When updating components, React JS does a shallow check on the state keys and values. What it means, is that it will detect a change when a key/value pair changes or updates, but won't detect a change in deeply nested objects within the state. Example:
this.state = {
visible: true,
users: {
{
id: 1,
name: 'john'
},
{
id: 2,
name: 'doe'
}
}
In the above code, React will detect a change in the visible key but not in the users object because it is a deeply nested object.
Now since you are using only the age of the patient, just pass the age as patientAge={this.state.info.age} and use it directly.
In your handler you are assigning the input value to state.age and not inside state.info.age
handle_age(event) {
const age = event.target.value;
this.setState(prevState => ({
info: { ...prevState.info, age }
}), console.log('age', this.state.info.age);
}
you are passing the state as a props to the child component so you are now entering the concept of when does change props will trigger a rerender.
if the prop is a primitive value (eg string or integer etc..) it will cause a rerender on every value change.
if the props are an array or like in your example an object, it will cause a rerender only when there's a new reference for that object or array
sense your only changing a property of an object and react dose shallow comparison of state and props, changing "age" will not trigger rerender in child.
you can do:
setState to entire info every time which will change his reference and trigger rerender at child
or
pass age and name separately as props to your child component
from what I see you are only using age in your child component so I would recommend option 2
like this:
<AddPatient
addNewPatient={this.state.info.age}
/>
const AddProduct = ({ addNewPatient}) => (
<FormGroup>
<Label for="className">Age</Label>
<Input
type="number"
name="sub_total"
id="sub_total"
placeholder=""
value = {addNewPatient}
/>
</FormGroup>
);

React Native - Update Parent State in Child with Dynamic Key

I am very new to both Javascript and React Native, and I am trying update a parent's state by using a callback function using a dynamic key to avoid writing multiple functions.
In the parent component, I pass this function to the child to child to update the parent's state based on user text input. This code achieves the desired result.
In Parent:
_setAge = (value) => {
this.setState({age: value})}
<ChildComponent name = 'Update Age' _setAge = { this._setAge.bind(this) } />
In Child:
//Other child code
<TextInput onChangeText = { (input) => {this.props._setAge(input)} }
//Etc.
However, I am looking for a way to pass a desired state key from the parent to the child to update the state dynamically. I have tried the following:
In Parent:
const ageKey = 'age'
_setAge = (value, stateKey) => {
this.setState({ [stateKey]: value })}
<ChildComponent name = 'Update Age' _setAge = { this._setAge.bind(this) } stateKey = ageKey } />
In Child:
//Other child code
<TextInput onChangeText = { (input) => this.props._setAge(input, this.props.stateKey)
//Etc.
However this doesn't work. My current work around is writing 6 functions for my 6 child components, each updating the desire state. However, while this would work for my basic app, I am looking for a way that is more scalable for future projects. Thank you!
In Parent
Instead of passing stateKey in props on child key directly pass the state key in onChageText method in child. the code would look like this->>>>
_setAge = (value, stateKey) => {
this.setState({ [stateKey]: value })}
<ChildComponent name = 'Update Age' _setAge = {this._setAge} /> // providing no statekey here
Inside the child
<TextInput onChangeText = { (input) => this.props._setAge(input, 'age') }
// here i know statekey for each child so i directly pass the key from child so i dont have to pass it as a props and then access it

How to avoid use of document.getElementById in Reactjs? [duplicate]

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