I'm doing a simple test where I have a name state in the parent component which I update when a button in the child is clicked. But this does not work, and I'm confused on if I'm doing something wrong.
Parent:
import React from "react";
import "./styles.css";
import Hello from "./Hello";
export default function App() {
const [name, setName] = React.useState();
const handle = item => {
setName(item);
};
console.log(name);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Hello test={handle} />
{name}
</div>
);
}
Child:
import React from "react";
export default function App() {
return (
<div>
<button onclick={() => this.props.test("TEST")}>Activate Lasers</button>
</div>
);
}
What am I doing wrong?
Pass the function as a prop.
import React from "react";
export default function App({ handle }) {
return (
<div>
<button onClick={() => handle('TEST')}>Activate Lasers</button>
</div>
);
}
and render the component like
<Hello handle={handle} />
// Get a hook function
const {useState} = React;
function Hello({ handle }) {
return (
<div>
<button onClick={() => handle('TEST')}>Activate Lasers</button>
</div>
);
}
function App() {
const [name, setName] = React.useState();
const handle = item => {
setName(item);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Hello handle={handle} />
{name}
</div>
);
}
// Render it
ReactDOM.render(
<App />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
import React from "react";
export default function App(props) {
return (
<div>
<button onClick={() => props.test("TEST")}>Activate Lasers </button>
</div>
);
}
It should be onClick Secondly you are using this which is pointing to stateless component that is undefined and have no property props within it.
First:
React events are written in camelCase syntax:
onClick instead of onclick.
second
the keyword this in react usually used in-class component, and using it in a functional component to bind it with where you are using it, so now you need to pass the props to the child so that you can access the function.
this should work
import React from "react";
export default function Hello(props) {
return (
<div>
<button onClick={() => props.test("TEST")}>Activate Lasers</button>
</div>
);
}
and third try to use the same naming for thing :) to be more readable
I hope I helped you
Related
So recently i tried making a chat app using React and firebase, to learn these tools. It goes very well, but for some reason it seems that whenever the database is active it spams read on the collection, even with no changes. I suspect some element is rerendering in an infinite loop, but i can't seem to fix it. Any suggestions for how i can display messages and only update whenever a new change is made to the collection?
import React, { useRef, useState } from 'react';
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
import 'firebase/compat/firestore';
import { useAuthState } from 'react-firebase-hooks/auth';
import { useCollectionData } from 'react-firebase-hooks/firestore';
import './App.css';
import Navbar from './Navbar';
firebase.initializeApp({
// My configs are here
})
const auth = firebase.auth();
const firestore = firebase.firestore();
function App() {
const [user] = useAuthState(auth);
return (
<>
<div className="App">
<Navbar />
</div>
<section className="place-content-end">
{user ? <ChatRoom /> : <SignIn />}
<SignOut />
</section>
</>
);
}
function SignIn() {
const signInWithGoogle = () => {
const provider = new firebase.auth.GoogleAuthProvider();
auth.signInWithPopup(provider);
}
return (
<>
<button className="sign-in ml-20 pl-5" onClick={signInWithGoogle}>Sign in with Google to join! </button>
<p className="ml-20 pl-5">Welcome to the chat!</p>
</>
)
}
function SignOut() {
return auth.currentUser && (
<button className="sign-out ml-20 pl-5" onClick={() => auth.signOut()}>Sign Out</button>
)
}
function ChatRoom() {
const messagesRef = firestore.collection('messages');
const query = messagesRef.orderBy('createdAt').limit(50);
const [messages] = useCollectionData(query, { idField: 'id' });
return (
<>
<div className="App ml-20 pl-5">
<h1>HELLO WORLD!</h1>
<main>
{messages && messages.map(msg => <DisplayMessage key={msg.id} message={msg} />)}
</main>
</div>
</>
);
}
function DisplayMessage(props) {
const { text, uid } = props.message;
return (<>
<div>
<p>{text}</p>
</div>
</>)
}
export default App;
And my firebase looks like this after a few minutes online on my local server
Try this and see if it works:
import React, { useRef, useState, useEffect } from "react";
import firebase from "firebase/compat/app";
import "firebase/compat/auth";
import "firebase/compat/firestore";
import { useAuthState } from "react-firebase-hooks/auth";
import { useCollectionData } from "react-firebase-hooks/firestore";
import "./App.css";
import Navbar from "./Navbar";
firebase.initializeApp({
// My configs are here
});
const auth = firebase.auth();
const firestore = firebase.firestore();
function App() {
const [user] = useAuthState(auth);
return (
<>
<div className="App">
<Navbar />
</div>
<section className="place-content-end">
{user ? <ChatRoom /> : <SignIn />}
<SignOut />
</section>
</>
);
}
function SignIn() {
const signInWithGoogle = () => {
const provider = new firebase.auth.GoogleAuthProvider();
auth.signInWithPopup(provider);
};
return (
<>
<button className="sign-in ml-20 pl-5" onClick={signInWithGoogle}>
Sign in with Google to join!{" "}
</button>
<p className="ml-20 pl-5">Welcome to the chat!</p>
</>
);
}
function SignOut() {
return (
auth.currentUser && (
<button className="sign-out ml-20 pl-5" onClick={() => auth.signOut()}>
Sign Out
</button>
)
);
}
function ChatRoom() {
const [messages, setMessages] = useState([]);
useEffect(() => {
const messagesRef = firestore.collection("messages");
const query = messagesRef.orderBy("createdAt").limit(50);
const [data] = useCollectionData(query, { idField: "id" });
setMessages(data);
}, []);
return (
<>
<div className="App ml-20 pl-5">
<h1>HELLO WORLD!</h1>
<main>
{messages.length > 0 &&
messages.map((msg) => (
<DisplayMessage key={msg.id} message={msg} />
))}
</main>
</div>
</>
);
}
function DisplayMessage(props) {
const { text, uid } = props.message;
return (
<>
<div>
<p>{text}</p>
</div>
</>
);
}
export default App;
I added useEffect in line 1. And changed your ChatRoom component.
Quick explanation
useEffect is part of the lifecycle of a Function Component. You give it a function to execute whenever the component mounts or props or state of the component changes. You decide this by what you add as the second argument of the useEffect call. If you give it an [] (empty array), it will only execute when the component is mounted. If you give, for example, [messages] (which a state created by useState) then the function will execute on component mount AND whenever messages change. React keeps track of this for you, this is why React is called react, if data changes it reacts to it and renders accordingly.
The above explanation is the basics of it. But there is also one more cool thing it can do:
If you return a function from inside of useEffect, this function will execute when the component is UNmounted. Like so:
useState(() => {
console.log("Component mounted");
return () => {
console.log("Component unmounted");
};
},[]);
In Class Components we have similar functions, they are called: componentDidMount, componentWillUnmount and others.You can read about it here React State & Lifecyle
Is it possible to consume multiple context in one component which is from two different component . I tried It but but I am not getting value
This is the component which consume two different context
import React from "react";
import { UserContext } from "../App";
import { NewContext } from "./CompY";
const CompX = () => {
return (
<div>
<UserContext.Consumer>
{(user) => {
return (
<>
<NewContext.Consumer>
{(username) => {
return (
<>
<h1>Hello {user.username} </h1>
<h2>This is CompX in the {user.name} component</h2>
<h3>This is {username} </h3>
</>
);
}}
</NewContext.Consumer>
</>
);
}}
</UserContext.Consumer>
</div>
);
};
export default CompX;
Here is the App.Js
You can use multiple contexts in functional component easily with useContext
Consumer and provider is bad practice in functional component
import { UserContext } from "../App";
import { NewContext } from "./CompY";
const CompX = () => {
const user = useContext(UserContext);
const newUser = useContext(NewContext);
....
}
I thought it was simple but looks like I´m doing something wrong
const name = "Mike"
...
<MyComponent
data={
<Picture /> + " " + name
}
/>
const Picture () => return <img src="..." />
const myComponent (props) => return props.data
i´m getting this output
[object Object] Mike
thank you!
You should separate the passing of both items into 2 different props, rather than joining them together (they are both different types - ReactNode, and string). The advantage of doing so is that there will be better type checking, especially if you are using TypeScript or PropTypes.
<MyComponent
data={<Picture />}
name={user.name}
/>
Then in the myComponent itself, you should do this if you are trying to print the name under the Picture.
const myComponent = ({ data, name }) => (
<>
{data}
{name}
</>
);
const Picture = () => <img alt="" src="" />;
const MyComponent = props => props.data;
export default function App() {
return (
const user = ...
<MyComponent
data={
<>
<Picture />
{user.name}
</>
}
/>
);
}
Any value should be passed thru {your value}
{user.name}
In this part of code, you shouldn't use return const MyComponent = props => props.data;
If you wanna return it in classic way write like this:
const MyComponent = props => {
return props.data
};
You can check this example:
Parent Component
import React, {Component, useEffect, useState} from 'react';
import {ChildComp} from "./ChildComp";
import {Hello} from "./Hello";
export class ParentComp extends Component {
render() {
return (
<div>
<ChildComp>
<Hello/>
</ChildComp>
</div>
);
}
}
Child Component
import React, {Component, useEffect, useState} from 'react';
export class ChildComp extends Component {
render() {
return (
<div>
{this.props.children}
</div>
);
}
}
Hello Component
import React, {Component, useEffect, useState} from 'react';
export class Hello extends Component {
render() {
return (
<div>
<h1>Hello World</h1>
</div>
);
}
}
You can pass a component as a prop. However, look at this line:
<Picture /> + " " + user.name
Since <AnyComponent /> results in a React element, and a React element is internally represented as an object, you're essentially doing this:
{} + " " + user.name
which is why you're seeing [object Object] in your output. What you should do is
function MyComponent({ pictureElement, name }) {
return (
<div>
{pictureElement} {name}
</div>
);
}
So I am using react-to-pdf to print Html tags to PDF the button is on one component and text is on another, I don't seem to have much knowledge as to how to make the button print index.js as pdf. I am pretty sure that something is wrong in ref and imports
button.js
import React, { Component } from "react";
import Pdf from "react-to-pdf";
const ref = React.createRef();
class Button extends Component {
render() {
return (
<React.Fragment>
<Pdf targetRef={ref} filename="code-example.pdf">
{({ toPdf }) => <button onClick={toPdf}>Generate Pdf</button>}
</Pdf>
</React.Fragment>
);
}
}
export default Button;
index.js
import React, { Component } from "react";
import "./styles.css";
import Button from "./button";
const ref = React.createRef();
class Index extends Component {
render() {
return (
<React.Fragment>
<div className="App">
<div ref={ref}>
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
</div>
</React.Fragment>
);
}
}
export default Index;
ReactDOM.render(<Button />, document.getElementById("top"));
const rootElement = document.getElementById("root");
ReactDOM.render(<Index />, rootElement);
Use like this:
import React from "react";
import ReactDOM from "react-dom";
import Pdf from "react-to-pdf";
const Button = React.forwardRef((props, ref) => {
return (
<React.Fragment>
<Pdf targetRef={ref} filename="code-example.pdf">
{({ toPdf }) => <button onClick={toPdf}>Generate Pdf</button>}
</Pdf>
</React.Fragment>
);
});
const App = () => {
let docToPrint = React.createRef();
return (
<div>
<div>
<Button ref={docToPrint} />
</div>
<React.Fragment>
<div className="App">
<div
ref={docToPrint}
style={{
borderRadius: "5px",
width: "600px",
height: "400px",
margin: "0 auto",
padding: "10mm"
}}
>
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
</div>
</React.Fragment>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Answer output: HERE
You can expose out props from the button to pass in the ref to the component you want saved to PDF. Here's a demo of a PDF button with targetRef and fileName props.
PDFButton.jsx
import React from "react";
import PropTypes from "prop-types";
import Pdf from "react-to-pdf";
// expose a targetRef prop and filename
const PDFButton = ({ children, filename, targetRef }) => (
<Pdf targetRef={targetRef} filename={filename}>
{({ toPdf }) => <button onClick={toPdf}>{children}</button>}
</Pdf>
);
PDFButton.propTypes = {
filename: PropTypes.string,
targetRef: PropTypes.any
};
PDFButton.defaultProps = {
filename: "code-example.pdf"
};
export default PDFButton;
App.js
import React, { createRef } from "react";
import PDFButton from "./PDFButton";
import "./styles.css";
export default function App() {
const pdfRef = createRef(); // create a single ref to pass to button
return (
<div ref={pdfRef} className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<p>This is a demo how to create a save to PDF button</p>
<PDFButton
fileName="awesomePDFButtonDemo.pdf"
targetRef={pdfRef}
>
Save to PDF!
</PDFButton>
</div>
);
}
I want to change innerHTML of a div, when I click on the button. I don't know why, but instead of getting an error, or getting the expected result it deletes to content and replacing it with "[object Object]".
How can I get it work?
import React from 'react';
import Login from './components/login.js';
import SignIn from './components/signin';
import './App.css';
function App() {
function LoginOnClick(){
document.getElementById("wrapper").innerHTML = <SignIn />;
}
return (
<div className="container" id="wrapper">
<button onClick={LoginOnClick}>Login</button>
<Login />
</div>
);
}
export default App;
You can make use of Hooks (Added n React 16.8).
import React, {useState} from 'react';
import Login from './components/login.js';
import SignIn from './components/signin';
import './App.css';
function App() {
const [signIn, setSignIn] = useState(false);
return (
<div className="container" id="wrapper">
{signIn ? <SignIn /> : <> //This is React Fragments syntax
<button onClick={() => setSignIn(true)}>Login</button>
<Login />
</>
}
</div>
);
}
export default App;
With react you don’t have to set the innerHtml to do this, instead the more typical way is to have internal state in your component and conditionally render your SignIn component based off that. To use state the component either needs to be class or use hooks, classes are more traditional so I changed the component to be a class.
To make a class a react component you need to extend the class with the React.Component, this is because react components have lots of internal behaviours that you need to include with your class for it to be considered a component.
So
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
signIn: false,
};
this.LoginOnClick = () => {
this.setState({ signIn: true });
};
}
render() {
if (this.state.signIn) {
return (
<div className="container">
<SignIn />
</div>
);
}
return (
<div className=“container”>
<button onClick={this.LoginOnClick}>Login</button>
<Login />
</div>
);
}
}
Here is a simple way to do it:
import {useState} from "react";
const App = () => {
const [txt, setTxt] = useState("");
setTxt(<p> 'Lorem ipsum dummy text blabla.' </p>);
return(
<div>
{txt}
</div>
)
}
export default App;