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
Related
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}/>
);
}
}
I'am creating React.js Weather project. Currently working on toggle switch which converts celcius to fahrenheit. The celcius count is created in one component whereas toggle button is created in another component. When the toggle button is clicked it must trigger the count and display it. It works fine when both are created in one component, but, I want to trigger the function from another component. How could I do it? Below is the code for reference
CelToFahr.js (Here the count is displayed)
import React, { Component } from 'react'
import CountUp from 'react-countup';
class CeltoFahr extends Component {
state = {
celOn: true
}
render() {
return (
<React.Fragment>
{/* Code for celcius to farenheit */}
<div className="weather">
<div className="figures">
<div className="figuresWrap2">
<div className="mainFigureWrap">
<CountUp
start={!this.state.celOn ? this.props.temp.cel : this.props.temp.fahr}
end={this.state.celOn ? this.props.temp.cel : this.props.temp.fahr}
duration={2}
>
{({ countUpRef, start}) => (
<h1 ref={countUpRef}></h1>
)}
</CountUp>
</div>
</div>
</div>
</div>
{/*End of Code for celcius to farenheit */}
</React.Fragment>
)
}
}
export default CeltoFahr
CelToFahrBtn (Here the toggle button is created)
import React, { Component } from 'react'
import CelToFahr from './CeltoFahr'
class CelToFahrBtn extends Component {
state = {
celOn: true
}
switchCel = () => {
this.setState({ celOn: !this.state.celOn })
}
render = (props) => {
return (
<div className="button" style={{display: 'inline-block'}}>
<div className="weather">
<div className="figures">
<div className="figuresWrap2">
<div className="mainFigureWrap">
<div onClick={this.switchCel} className="CelSwitchWrap">
<div className={"CelSwitch" + (this.state.celOn ? "" : " transition")}>
<h3>C°</h3>
<h3>F°</h3>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default CelToFahrBtn
Here when I click on switchCel it must trigger the celcius to fahrenheit value and vice-versa. How to do it? Any suggestions highly appreciated. Thanks in advance
I would have the celToFahr be the parent component of the celToFahrBtn and then pass the function you want to invoke via props
<CellToFahrBtn callback={yourfunction}/>
What else could you do is having a common parent for these to components where you would again do the execution via props and callbacks
The 3rd option would be having a global state which would carry the function like Redux or Reacts own Context. There again you would get the desired function via props and you would execute it whenever you like. This is the best option if your components are completely separated in both the UI and in source hierarchically, but I don't think this is the case in this case.
https://reactjs.org/docs/context.html
These are pretty much all the options you have
To achieve this you'd need to lift your state up and then pass the state and handlers to the needed components as props.
CeltoFahr & CelToFahrBtn would then become stateless components and would rely on the props that are passed down from TemperatureController
class TemperatureController extends Component {
state = {
celOn: true
}
switchCel = () => {
this.setState({ celOn: !this.state.celOn })
}
render () {
return (
<React.Fragment>
<CeltoFahr celOn={this.state.celOn} switchCel={this.state.switchCel} />
<CelToFahrBtn celOn={this.state.celOn} switchCel={this.state.switchCel}/>
</React.Fragment>
)
}
}
It's probably better explained on the React Docs https://reactjs.org/docs/lifting-state-up.html
See this more simplified example:
import React, {useState} from 'react';
const Display = ({}) => {
const [count, setCount] = useState(0);
return <div>
<span>{count}</span>
<Button countUp={() => setCount(count +1)}></Button>
</div>
}
const Button = ({countUp}) => {
return <button>Count up</button>
}
It's always possible, to just pass down functions from parent components. See Extracting Components for more information.
It's also pretty well described in the "Thinking in React" guidline. Specifically Part 4 and Part 5.
In React you should always try to keep components as dumb as possible. I always start with a functional component instead of a class component (read here why you should).
So therefore I'd turn the button into a function:
import React from 'react';
import CelToFahr from './CeltoFahr';
function CelToFahrBtn(props) {
return (
<div className="button" style={{ display: 'inline-block' }}>
<div className="weather">
<div className="figures">
<div className="figuresWrap2">
<div className="mainFigureWrap">
<div onClick={() => props.switchCel()} className="CelSwitchWrap">
<div
className={'CelSwitch' + (props.celOn ? '' : ' transition')}
>
<h3>C°</h3>
<h3>F°</h3>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
export default CelToFahrBtn;
And you should put the logic in the parent component:
import React, { Component } from 'react';
import CountUp from 'react-countup';
import CelToFahrBtn from './CelToFahrBtn';
class CeltoFahr extends Component {
state = {
celOn: true
};
switchCel = () => {
this.setState({ celOn: !this.state.celOn });
};
render() {
return (
<>
<div className="weather">
<div className="figures">
<div className="figuresWrap2">
<div className="mainFigureWrap">
<CelToFahrBtn switchCel={this.switchCel} celOn={celOn} />
</div>
</div>
</div>
</div>
</>
);
}
}
I'm trying to make suggested search entries display in from Google Api appear in the div with the id Suggested-Places using input values from the input tag with the id SearchBar. Unfortunately,the event handlers aren't firing.
here is my code
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import logo from './logo.svg';
import './App.css';
import MdShoppingCart from 'react-icons/lib/md/shopping-cart'
export default class HeaderMin extends Component{
constructor(props) {
super(props);
this.suggestedPlaces=[];
this.state={
suggestions:this.suggestedPlaces
}
this.userLocationInput=this.userLocationInput.bind(this);
this.suggestedLocations=this.suggestedLocations.bind(this);
}
componentDidMount() {
this.address=this.refs.inputBox.value;
const searchBar=ReactDOM.findDOMNode(this.refs.inputBox);
searchBar.addEventListener('keyUp keyPress keyDown',this.userLocationInput)
}
suggestedLocations(location){
this.suggestedPlaces.push(location);
}
userLocationInput() {
const key="&key=AIzaSyCvfy3g8ljGFtVyfCP9idWbwRo_-HASt_0",url="https://maps.googleapis.com/maps/api/place/textsearch/json?query=";
let query=this.address;
const endPoint=url+query+key;
return fetch("http://localhost:8080/"+url+query+key)
.then((res)=>res.json())
.then((res)=>res.results.map((loc)=>this.suggestedLocations(loc.formatted_address)))
}
render(){
return(
<div className="myheader header-min">
<img src="http://res.cloudinary.com/www-mybukka-com/image/upload/v1505151382/logo_m8ik1x.png" id="logo" alt="logo"/>
<div className="search-box search-box-min">
<div>
<input type='text' ref="inputBox" id="SearchBar" defaultValue='search your location'/>
<div id="Suggested-Places">{this.state.suggestions.map((location)=><p>{location}</p>)}</div>
</div>
<button className="btn-sml btn-red"></button>
</div>
<div className="header-top-button header-top-button-min">
<button ></button>
<button className="btn-red"></button>
<MdShoppingCart className="shopping-cart"/>
</div>
</div>
)
}
}
React use synthetic events, so your regular events won't probably work. Use the regular React way if you want it to work.
You should be using Reacts built-in event handler props. Also, you can simplify your code:
<input type='text' ref="inputBox" id="SearchBar" defaultValue='search your location' onKeyDown={this.userLocationInput}/>
You probably don't need to bind the same event to all 3 key events, onKeyDown should be enough. If you need the other ones as well, you can use onKeyPress and onKeyUp.
EDIT:
And after looking at userLocationInput, you'll need to make a small change:
userLocationInput(e) {
const key="&key=AIzaSyCvfy3g8ljGFtVyfCP9idWbwRo_-HASt_0",url="https://maps.googleapis.com/maps/api/place/textsearch/json?query=";
let query = e.target.value; // or this.refs.textInput.value
const endPoint=url+query+key;
return fetch("http://localhost:8080/"+url+query+key)
.then((res)=>res.json())
.then((res)=>res.results.map((loc)=>this.suggestedLocations(loc.formatted_address)))
}
When you bind this.address in the componentDidMount to this.refs.textInput.value, that's a one time assignment. this.address won't update everytime the value gets changed. So instead you should be using e.target.value or this.refs.textInput.value.
One last note, string refs are being deprecated in React so you should be using a ref callback instead.
<input ref={ ref => this.textInput = ref } />
...
// Getting value from input
this.textInput.value;
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
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.