Why use Ref when we can use querySelector? - javascript

I am new in the React world and was learning about Refs. The React doc says that:
Refs provide a way to access DOM nodes or React elements created in
the render method.
I think that we can do the same thing with the querySelector function.
The React doc gives an example of how can we use Refs to focus on the input element.
Here's the code ->
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// create a ref to store the textInput DOM element
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// Explicitly focus the text input using the raw DOM API
// Note: we're accessing "current" to get the DOM node
this.textInput.current.focus();
}
render() {
// tell React that we want to associate the <input> ref
// with the `textInput` that we created in the constructor
return (
<div>
<input
type="text"
ref={this.textInput} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Doing the same thing with querySelector ->
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
focusTextInput = () => {
const input = document.querySelector("input");
input.focus();
};
render() {
return (
<div>
<input type="text" ref={this.textInput} />
<button type="button" onClick={this.focusTextInput}>
Focus the Text Input
</button>
</div>
);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Since, the framework itself has exposed a method to do something which can be done through vanilla javascript, it certainly has added advantages. One of the scenario I can think of is using React.forwardRef which can be used for:
Forwarding refs to DOM components
Forwarding refs in higher-order-components
As explained in the React docs itself:
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;

you don't need react or angular to do any web development, angular and react give us a wrapper which will try to give us optimize reusable component,
all the component we are developing using react can be done by web-component but older browser don't support this.
i am listing some of benefit of using ref in React
this will be helpful for if you are using server side rendering, Be careful when you use Web API(s) since they won’t work on server-side Ex, document, Window, localStorage
you can easily listen to changes in state of object, you don't have
to maintain or query the DOM again and again and the framework will
do this for you.

Related

How to use Props to jQuery in React component

I hope you understand this simple example.
I tried to change the background color of my HTML element on first render by handling it in React Component with a bit help of jQuery.
This is the code inside my React Component (the props is passed from state of Parent Component):
class QuoteBox extends React.Component {
constructor(props) {
super(props)
}
componentDidMount() {
$('#root').css('background-color', this.props.color);
$('#text').css('color', this.props.color);
$('#author').css('color', this.props.color);
}
render() {
return (
<div>
<div id="quote-box" className="quote-box">
<div className="quote">
<span id="text" class="quote-text">{this.props.quote}</span>
</div>
<div id="author" className="quote-author">
<span>- {this.props.author}</span>
</div>
</div>
)
}
The code inside componentDidMount() seem doesn't recognize the this.props.color. But if change to $('#root').css('background-color', 'green'); it's immediately change the background to green on first render.
But in render() it recognize another props.
So what did I do wrong? Is there a way to target HTML element using jQuery inside React?
This most likely happens, because on the first render the props.color is not set. It is most likely, set on the parent element causing a re-render of the QuoteBox, which will not run the componentDidMount which is only run on the first render.
Besides the fact that you should not mix jQuery DOM manipulation with React, just to test your scenario, try using componentDidUpdate instead and it will most likely fix your issue.

Next.js custom javascript not running on it's own page

I want to add a custom javascript in my next.js page
but my script seems doesn't load properly.
this is not my full code,
but i hope it can represent my problem.
export default class extends React.Component {
render() {
return (
<div>
<div id="myDiv">
<p>Inside My Div</p>
</div>
<script dangerouslySetInnerHTML={{
__html: '$(function() {
document.getElementById("myDiv").style.display = "block";
})'
}} />
</div>
)
}
}
why i can't change style.display like code above ?
As you are using react it's better to use the api that react provided. There is a method called Ref.
For that it's better to convert the class based component to functional component. Then import useRef from react
Declare the ref
const myRef = useRef()
Now assign ref to your div
<div ref={myDivRef} id="myDiv">
Now in your function access it like bellow
myDivRef.current.style.display = 'block'
First of all, react will insert this with innerHTML which means it will inserted as DOM text. Second of all, if you're using Next.js, you should have in mind that on server side, you don't have access to the document object and you should check if it is not undefined.
As a solution, why don't you try putting it in componentDidMount as here you have the document object and you can change the style.
componentDidMount() {
document.getElementById("myDiv").style.display = "block";
}

How to perform a click on element2 when element1 is clicked in React js?

I have two independent elements (not a parent-child).
Is it possible to accomplish the behavior such that StyledDropDownInputAsync is actually clicked on clicking StyledSearchInput.
<StyledSearchInput/>
<StyledDropdownInputAsync searchIcon
className="options"
placeholder="Search"
loadOptions={loadOptions}
onChange={this.handleSelection}
cache={{}}
filterOptions={(options) => (options)}>
</StyledDropdownInputAsync>
Wrap them in a parent component, define a click state, pass the click state to StyledDropdownInputAsync, pass the click action to StyledSearchInput.
const ParentComp = () => {
const [isClicked, setClick] = useState(false);
return (
<>
<StyledSearchInput onClick={() => setClick(true)}/>
<StyledDropdownInputAsync isClicked={isClicked} {...otherProps}>
</StyledDropdownInputAsync>
</>
);
};
This called Lifting State Up in ReactJS.
You can use ref to implement such things,
according to documentation, Refs provide a way to access DOM nodes or React elements created in the render method.
you can read more about refs using this link.
for implementing such functionality, please refer to this code sandbox

React: change modal's content without passing entire DOM elements as a props?

I am new to React and currently I am trying to build a Two-Fact-Auth Modal-like UI in my project.
The Modal looks relatively sample as well.
Title
A message
Modal Content(which can be a inputbox, or one or more drop-down selections, or just displaying string)
A Button
Imagine there are some modals: First one ask you to enter your phone number. After you typed your phone number, it get direct to second modal and second modal will display the phone number you typed and ask you to confirm, and third modal will display other things and so on.
My approach to this is to build a my own modal component using ReactStrap.
export default class ModalControl extends React.Component{
render(){
return(
<div>
<Modal isOpen={this.props.isOpen}>
<ModalHeader >{this.props.title}</ModalHeader>
<ModalBody>
<p>{this.props.message}</p>
<p>{"Content that is change dynamically"}</p>
</ModalBody>
<ModalFooter>
<Button color="info" onClick={() => this.props.clickAction>{this.props.buttonLabel}</Button>
</ModalFooter>
</Modal>
</div>
)
}
}
However, One of the problem I have is to change the modal content. Since the Modal does not know if itself contains a selection, 'A input box', or 'Just a string', what should i do so the ModalControl can take content that is dynamically changing?
My attempt:
I tried to pass entire DOM elements as a string to modal and parse it in the modalControl. However, I have read a SO post that saying passing Dom Elements is not recommended in React.
In my main, i have something like this, but apparently it is not rendering
<ModalControl
isOpen={true}
title={"Code Authentication"}
message={"For your security, we need to verify your identity by sending a code to your phone number"}
buttonLabel={"Verify Code"}
>
<div className="row">
<div className="col-sm-6">
{this.getPhoneList()}
</div>
<div className="col-sm-6">
{this.getMethodList()}
</div>
</div>
</ModalControl>
What i want to achieve: How do I implement a modal class whose modal content that is dynamically changing? Since I am new to React, I am not sure if this is the best practice. If it is not, is there a better approach?
You have to maintain a state which holds variables that can dynamically change.
First you set the state.
constructor(props) {
super(props);
this.state = {
variableX: "default string"
};
}
You can then use React's setState() method to update the state
this.setState({
variableX: "updated string"
});
In your render method you can then access the variable
render() {
const { variableX } = this.state;
return (
<div>{variableX}</div>
);
}
You can also pass the state as a prop to child components
// Parent component
render() {
const { variableX } = this.state;
return (
<div>
<ChildComponent variableX={variableX} />
</div>
);
}
// Child component
render() {
const { variableX } = this.props;
return (
<div>
{variableX}
</div>
);
}
One thing to note is that you never want to mutate the state. You can search many articles regarding this using the terms "react state immutability"
You should read: https://reactjs.org/docs/state-and-lifecycle.html
In a large application where it's more difficult to maintain a state, and where you don't want to keep passing properties all the time, you can consider using Redux with React:
https://redux.js.org/basics/usagewithreact

How do I test a React component that calls a function on its parent, which changes props on the component?

The problem:
component Child's props are passed down as the values of Parent's state.
Child has a method that calls a method on Parent, which updates the state of Parent.
When Parent's state updates, one of Child's prop values change. as in: <Child prop1={this.state.prop1}>
What is the right way to go about testing that this process is happening as expected?
Here's some example code to make the problem clearer:
//App.js
import React, { Component } from 'react';
import Content from './Content';
class App extends Component {
constructor(props){
super(props)
this.state = {
page: 'home',
}
}
gotoAbout(){
this.setState({
page: 'about',
})
}
render() {
return(
<Content page={this.state.page} gotoAbout={this.gotoAbout.bind(this)} />
)
}
}
As you can see, the parent component App passes a prop, and a function that can change the value of that prop to its child component, Content.
The Content component would then do something like this:
//Content.js
import React, { Component } from 'react';
class Content extends Component {
constructor(props){
super(props)
}
gotoAbout() {
this.props.gotoAbout()
}
render(){
if(this.props.page = 'home'){
return(
<div>
<p>this is the home content</p>
<button onClick={this.gotoAbout}></button>
</div>
)
} else {
return(
<p>this is the about content</p>
)
}
}
}
The above is a simplified example, but I think it gets the point across. What would be the best way to write a test for this kind of component-prop flow?
I generally first start testing the components in isolation using shallow rendering with its expected functionality and then test components with composition.
e.g To test Content component
1.test whether it behaves correctly for props or state changes
2.test if it performs event's correctly such as button click or any other events through simulation
const wrapper
= shallow(
<Content
page={"home"}
gotoAbout={()=>{ console.log("fake goAbout")}}
/>
);
Now Check if rendered structure is matched as expected for prop page={"home"}
expect(wrapper.containsMatchingElement(
<div>
<p>this is the home content</p>
<button onClick={this.gotoAbout}></button>
</div>
)).to.equal(true);
Similarly test for other prop page={"about"} whether content renders correctly or not.
wrapper.setProps({ page: 'about' });
expect(wrapper.containsMatchingElement(
<p>this is the about content</p>
)).to.equal(true);
After that you can test for button click events
clickCount = 0;
const wrapper
= shallow(
<Content
page={"home"}
gotoAbout={()=>{ clickCount = clickCount + 1;}}
/>
);
Now you can check whether clickCount is greater than zero, after simulating the click event.
wrapper.find('button').simulate('click');
After that you may start testing the App component.
const wrapper = shallow(<App />);
const childWrapper = wrapper.find('Content');
After that you may create another Content component separately via shallow rendering and match those two for equal html structure, props, states etc.
const twrapper
= shallow(
<Content
page={"home"}
gotoAbout={()=>{ console.log("fake goAbout")}}
/>
);
expect(twrapper.html()).to.equal(childWrapper.html());
You can also check whether prop is passed correctly to rendered child elements -
expect(childWrapper.prop('page')).to.equal("home");
There may other better way available as well for testing the react components and these are just simple test examples.
Enzyme provides lots of ways to test your components and there is no hard and fast rule I guess. But you should at least test the expected functionality and features of your component.
Also your test cases make sure that any new changes being made to the component don't break your test specification.

Categories