Material Components Web and React: Cannot read property 'querySelector' of undefined; - reactjs

I am trying to build a Modal component taking advantage of React and Material Components Web. The visibility of Modal is inherited from the parent state as props:
import React, {Component} from 'react';
import {MDCFormField} from '#material/form-field/';
import {MDCTextfield} from '#material/textfield/';
import './modal.scss';
export default class Modal extends Component {
componentDidMount() {
this.email = new MDCTextfield(this.email);
this.pwd = new MDCTextfield(this.pwd);
}
componentWillUnmount() {
this.email.destroy();
this.pwd.destroy();
}
render() {
if (this.props.isModalOpen){
return (
<div id="modal-container">
<div id="mask"></div>
<div id="modal">
<form className="mdc-form-field">
<div ref={(div) => {this.email = div}} className="mdc-textfield">
<label type="email" htmlFor="email" className="mdc-textfield__label">Your email</label>
<input type="text" id="email" className="mdc-textfield__input"/>
<div className="mdc-textfield__bottom-line"></div>
</div>
<div ref={(div) => {this.pwd = div}} className="mdc-textfield">
<label htmlFor="pw" className="mdc-textfield__label">Password</label>
<input type="password" id="pw" className="mdc-textfield__input" required minLength={8}/>
<div className="mdc-textfield__bottom-line"></div>
</div>
</form>
</div>
</div>
);
} else {
return null
}
}
}
As soon as the app initialise an error appears "TypeError: Cannot read property 'querySelector' of undefined”
Of course it does since the since as the Modal is returning null.
so i tried to initialise Material Components as
componentDidMount() {
this.email = this.email && new MDCTextfield(this.email);
this.pwd = this.pwd && new MDCTextfield(this.pwd);
}
In this case the error is not thrown anymore but obviously the components are not initialised.
I did not come up with a pattern to solve this problem. Also a css approach did not work ( the idea was to toggle .someClass {display: none} from the main container ).
/** SOLVED **/
Ok I came up with a working pattern to solve the problem.
The problem was in the architecture of the app and the encapsulation of the components was not appropriate.
This is a parent component called Modal:
import React, {Component} from 'react';
import EmailField from './email-field';
import PwdField from './password-field';
import './modal.scss';
export default class Modal extends Component {
render() {
if (this.props.isModalOpen){
return (
<div id="modal-container">
<div id="mask"></div>
<div id="modal">
<form className="mdc-form-field">
<EmailField />
<PwdField />
</form>
</div>
</div>
);
} else {
return null
}
}
}
Than we have Children components as
import React, {Component} from 'react'
import {MDCTextfield} from '#material/textfield/';
export default class EmailField extends Component {
componentDidMount(){
this.email = new MDCTextfield(this.email);
}
componentWillUnmount(){
this.email.destroy();
}
render(){
return(
<div ref={(div) => {this.email = div}} className="mdc-textfield">
<label type="email" htmlFor="email" className="mdc-textfield__label">Your email</label>
<input type="text" id="email" className="mdc-textfield__input"/>
<div className="mdc-textfield__bottom-line"></div>
</div>
);
}
}
I was trying to init and destroy from a different scope of MDCTextfield elements.

You should try out the react version of this library.

Try to console.log(this.email). Afterwards you will see, that ref that you try to pass is not a selector, this is expected when you initialise matertial-components.
#Fawaz answer is correct, but you can instantiate it the way you want, even on div.
Showcase: https://www.webpackbin.com/bins/-KwEsyJR3O3eW50gX2pq
Did you using components like i am using there?
Use this for reference:
Material Components Textfield, Manual Instantiation

Related

Adding a class into another class's return statement

Im new to react, but I am wondering if there is a solution to creating a nesting class in the return statement of an existing class? Ive tried to look for other solution and had no choice but to ask this here.
Assuming in the return class in App.js:
import { useRef, useEffect} from "react";
import { MapContainer } from "./maps";
import { GoogleApiWrapper } from 'google-maps-react';
return (
<div class="body">
<div class="background"></div>
<div class="topnav">
<input id="seachbar" name="seachbar" type="text" placeholder="Search.." ref={inputRef} ></input>
<button onclick="document.getElementByName('seachbar').name = ''" class='removeButton'>X</button>
<button type="submit" class='searchButton'>Search</button>
</div>
<br></br>
<br></br>
<br></br>
<div class="content">
<MapContainer/>
</div>
</div>
);
I had tried to add the class inside it but kept getting an error stating that export has to be outside the statement. Is there a function that I can use to nest it inside? Just adding more context I am trying to add in google maps into the div section but the google maps contain 2 different exports.
Assuming in the return class in maps.js:
export class MapContainer extends Component {
render() {
return (
<Map
google={this.props.google}
zoom={14}
style={mapStyles}
initialCenter={
{
lat: 3.1474767381135407,
lng: 101.69958884212062
}
}
/>
);
}
}
export default GoogleApiWrapper({
apiKey: 'AIzaSyDkXk3MuVjovvaVAnl2wt2j_2ES-INLJ0s'
})(MapContainer);
I want to use both the section of the export in order to generate a map from map.js into App.js. However when I call in App.js after importing. It would say "loading map" but unable to do so.

Squaring the value of an input box in React

I am new to learning React and doing a little test project each day. Today, I am trying to create an input box that when I click a Submit button, it alerts the square of a number. Nice and simple. But, I am trying to do this without using State. Just trying to understand how. Here is my code but something is missing. I think I am close!
Any ideas?
import { render } from '#testing-library/react';
import React from 'react';
class App extends React.Component {
sayHi = props => {
alert(this.props.mySentProps);
};
squareTheNumber = () => {
alert('this is the squared number'+ );
};
render() {
return (
<div>
<div onClick={this.sayHi}>
<h1>Hello World</h1>
</div>
<div>
<input type="text" placeholder={'Enter a number to square'} />
</div>
<div>
<button onClick={this.squareTheNumber}>Submit me</button>
</div>
</div>
);
}
}
export default App;
Try this:
import React from "react";
import "./styles.css";
class App extends React.Component {
sayHi = (props) => {
alert(this.props.mySentProps);
};
squareTheNumber = (event) => {
event.preventDefault();
// Should be the same as input's "name" or "id" property
// Docs: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/elements
const { number } = event.target.elements;
alert(`this is the squared number: ${number.value ** 2}`);
};
render() {
return (
<div>
<div onClick={this.sayHi}>
<h1>Hello World</h1>
</div>
<div>
<form onSubmit={this.squareTheNumber}>
<input
name="number"
type="text"
placeholder="Enter a number to square"
/>
<button type="submit">Submit me</button>
</form>
</div>
<div></div>
</div>
);
}
}
export default App;
P.S.: render from #testing-library/react is used for testing purposes only. See docs here. Class components have their own field with the same name.
As said, there is no clean way to do it without state or any extensions. The best way is to use state and make things clean. But another way you can do it is to use JQuery.
For example:
You can assign the <input> an id, say myId. Then you do this:
var content = $('#myId').content;
And then you can change the content in the p by assigning it a new value.
But using JQuery kinds of defeats the purpose of React, so I would recommend using state.
You can use refs to access mounted elements directly.
https://reactjs.org/docs/refs-and-the-dom.html

How to get array in class component to another file in react?

The real situation is that I want to get the value of radio button in one file and let them show in another file, therefore I can let user know what they select before.
The code that use select the radio button:
class Usurvey extends Component {
constructor(props){
super(props);
this.state = {
uid: uuid.v1(),
studentName: '',
answers: {
answer1: '',
answer2: '',
answer3: ''
},
isSubmitted: false
};
this.nameSubmit = this.nameSubmit.bind(this);
this.answerSelected = this.answerSelected.bind(this);
this.questionSubmit = this.questionSubmit.bind(this);
}
render(){
<form onSubmit={this.questionSubmit}>
<div className="card">
<div className="video">
<ReactPlayer url={Array[0].url} />
</div>
<label>Do you think the video is fake or real? </label> <br />
<input type="radio" name="answer1" value="real" onChange={this.answerSelected} />Real
<input type="radio" name="answer1" value="fake" onChange={this.answerSelected} />Fake
</div>
<div className="card">
<div className="video">
<ReactPlayer url={Array[0].url} />
</div>
<label>Do you think the video is fake or real? </label> <br />
<input type="radio" name="answer2" value="real" onChange={this.answerSelected} />Real
<input type="radio" name="answer2" value="fake" onChange={this.answerSelected} />Fake
</div>
return(
<div>
{studentName}
{questions}
</div>
);
}
}
export default Usurvey;
The code file I want to input the value of radio button.
'use strict'
import React, { useState } from 'react';
import ReactPlayer from 'react-player';
import Array from '../Array';
import Usurvey from '../Usurvey2';
export default () => {
return (
<div>
<div className="video">
<ReactPlayer url={Array[0].url} playing/>
<div>
<p>Your guess: </p>
<p>{Usurvey.props.Answer[0]}</p>
<p>Right Answer:</p>
<p>{Array[0].name}</p>
</div>
</div>
</div>
)
}
Now I tried to import the Usurvey from the last file and use prop to get answer, but it does not work
Props are not static or hardcoded data that can be accessed by just import the file. Moreover, this is not a way to pass props from a parent component to a child component.
Either you need to lift up the state in Usurvey component to its parent component from where you can pass it to the component where you want the radio button's value.
Or you can pass onSubmit from handler as a prop to Usurvey component from its parent component. The parent component will store the form data and pass it to the required component.
Or if both the component are at a different level of hierarchy then you can also use redux instead of passing it to each component in the path.
You can read more about components and props over here.
Edit
class ParentComponent extends Component {
state = {
submittedQuestion: {} // depending on your logic you can store all the
questions or just one.
}
onQuestionSubmit = () => {
// store data in state.
}
render (){
const {submittedQuestion} = this.state;
return (
<Usurvey onQuestionSubmit={this.onQuestionSubmit} />
<QuestionSubmittedView submittedQuestion={submittedQuestion}/>
);
}
}

hooks are not rendering in react

In react, I am trying to use react hooks.I have created one hook which contains a form and I am importing that in class based component and rendering it there. But hooks is not rendering in contact component
//contactushook.js
import React from 'react';
const contactUshook = props => {
return <React.Fragment>
<form>
<div>
<input id="name" type="text" placeholder="enter the name"></input>
</div>
<div>
<input id="email" type="email" placeholder="enter the email"></input>
</div>
<div>
<input id="message" type="text-area" placeholder="Type message here"></input>
</div>
<button type="submit">Submit</button>
</form>
</React.Fragment>
}
export default contactUshook;
//contact.js
import React, { Component } from 'react';
import contactUshook from './hooks/contactushook';
class ContactComponent extends Component {
render() {
return (
<div>
<h4>hook</h4>
<contactUshook></contactUshook>
</div>
);
}
}
export default ContactComponent;
Your code is pretty working. You've should name your custom component <contactUshook> starting with capital letter, so React knows that it is custom component and not html tag.
Note: Always start component names with a capital letter.
React treats components starting with lowercase letters as DOM tags. For example, represents an HTML div tag, but represents a component and requires Welcome to be in scope.
So this will fix you issue
import React, { Component } from 'react';
import ContactUshook from './hooks/contactushook';
class ContactComponent extends Component {
render() {
return (
<div>
<h4>hook</h4>
<ContactUshook></ContactUshook>
</div>
);
}
}
export default ContactComponent;
And as already mentioned, your code does not deal with hooks. You created ordinary components.
Working sample is here

Reactjs: how to put the html template in a separate file?

I'm new to React. I'm much more familiar with Angular2+. In Angular, every component has a separate html file. However, in React, I see that render function itself includes the html template. For example,
import React, { Component } from 'react';
class HelloWorld extends Component {
render() {
return (
<h2> Hello World </h2>
);
}
}
export default HelloWorld;
Well I want to take
<h2> Hello World </h2>
outside the js file and put it in a separate html and import the html file to render function, for example
render() {
return (
import content of helloworld.html
);
}
Do you know how to do it?
In React you would typically make a child component and import it into the parent component.
Since your child component here would just be static markup i.e <h2>Hello World</h2>, you don't need to worry about component state.
Therefore, you could do the following:
make a functional (aka stateless or dumb) component for your text. You could name it HelloWorldText as an example.
import this functional component into your parent component HelloWorld.
Your functional component would look something like this:
HelloWorldText.js
const HelloWorldText = () => ( <h2> Hello World </h2> );
export default HelloWorldText;
Then you would import this functional component HelloWorldText into your parent component HelloWorld like so:
HelloWorld.js
import React, { Component } from 'react';
import HelloWorldText from './path/to/HelloWorldText';
class HelloWorld extends Component {
render() {
return (
<HelloWorldText />
);
}
}
export default HelloWorld;
Here's a CodePen Demo with this code.
Unfortunately on CodePen you can't export and import components, but hopefully it still gives you an idea on how to use a child component inside a parent component.
Note: In React you want your components to be as general as possible. You would probably end up making a Text component instead of a HelloWorldText component.
Then you would pass text dynamically into the Text component using props.
Here is a CodePen Demo of this in action.
You can move the JSX part into a separate file and import that file in your component class
Here's an example
Signin.jsx
import React from 'react';
export const SigninJsx = () => {
return (
<div className="container">
<form className="form-signin">
<h2 className="form-signin-heading"> Please sign in </h2>
<br />
<label htmlFor="inputEmail" className="sr-only"> Email address
</label>
<input type="email" id="inputEmail" onChange={this.handleEmailChange} className="form-control" placeholder="Email address" required autoFocus />
<br />
<label htmlFor="inputPassword" className="sr-only"> Password</label>
<input type="password" id="inputPassword" onChange={this.handlePasswordChange} className="form-control" placeholder="Password" required />
<br />
<button className="btn btn-lg btn-primary btn-block" onClick={this.signIn} type="button"> Sign in
</button>
</form>
</div>
)
}
Signin.js
import React, { Component } from 'react';
import {SigninJsx} from './Signin.jsx';
export class Signin extends Component {
constructor(props){
super(props);
this.handleEmailChange = this.handleEmailChange.bind(this);
this.handlePasswordChange = this.handlePasswordChange.bind(this);
this.state = {
email:'',
password:''
};
this.signIn = this.signIn.bind(this)
}
handleEmailChange(e){
this.setState({email:e.target.value})
console.log("Error Change");
}
handlePasswordChange(e){
this.setState({password:e.target.value})
}
signIn(){
alert('Email address is ' + this.state.email + ' Password is ' + this.state.password);
}
render() {
return (
<SigninJsx />
)
}
}
Please checkout this Medium link
This will be your React component declaration and
you need to import the template.js file inside here and render it in context of the component 'Abc' (index.jsx):
import template from './template';
export default class Abc extends React.Component {
render() {
return template.call(this)
}
}
Separate js file will have template as follows (template.js):
import styles from './styles.module.css';
const template = () => (
<div className={styles.outerContainer}>
<div className={styles.middleContainer}>
<div className={styles.innerContainer}>Hello, World</div>
</div>
</div>
);
export default template;
Additionally, we can import CSS modules inside the template and maintain them in serapate file as well, as follows (styles.module.css):
.outerContainer {
backgroundColor: red;
}
/* etc... etc... */
for now , it is not possible to load template from html file.

Resources