Render a component from another class using function - javascript

So, I have a class like this:
class Blah extends Component {
constructor(props) {
super(props);
}
handleComponent = (event) => {
let divid = event.target.getAttribute('id');
if (divid === 'col') {
// I want to render component by this condition
} else if (divid === 'ro') {
// or I want to render component by this condition
} else {
//or I want to render component by this condition
}
};
render() {
const { classes } = this.props;
return (
<div>
<div id = 'col' onClick={this.handleComponent}>Sheep</div>
<div id = 'ro' onClick={this.handleComponent}>Cow</div>
<div id = 'ball' onClick={this.handleComponent}>Dog</div>
{ I want to render my component here after click }
</div>
);
}
}
I have another class written on top of this:
class Flow extends Component {
constructor(props){
super(props);
};
render() {
return(
<div style={{background:'somecolor'...blah blah}}>Clap</div>
);
}
}
And I am Passing this by:
var foo = withStyles(styles)(Flow)
I have tried returning components but I am not getting anywhere.
I can use ternary operator but it still will render only one of two but I have three component have three design for each of them.
I want to render one of them to render on some condition as stated above.
If I use states that for toggle that will too have two components for render. Don't go on the code, this is made up, So any Ideas ? Fragments ? Any help will be appreciated. Thank you.

To render component by condition simply use switch statement.
In my example we use state to store current active component.
renderMyComponent method takes care of rendering one of three possible components.
handleChange method changes current state and triggers new render of App component.
This example use class properties plugin.
renderMyComponent = () => {
means autobind and is the same as using in constuctor method
this.renderMyComponent = this.renderMyComponent.bind(this);
Working example:
const ComponentOne = () => <div>Hi, i am component one</div>;
const ComponentTwo = () => <div>Hi, i am component two</div>;
const ComponentThree = () => <div>Hi, i am component three</div>;
class App extends React.Component {
state = { current: 0 }
renderMyComponent = () => {
// Our switch conditional render
switch(this.state.current) {
case 0:
return <ComponentOne />;
case 1:
return <ComponentTwo />;
case 2:
return <ComponentThree />;
default:
return null;
}
}
handleChange = (event) => {
// We are looking for data-trigger attribute
// In this example we expect type number but trigger holds string
// That's why we 'cast' to a number using Number()
const current = Number(event.target.dataset.trigger);
// Sets new state of current component and triggers new render
this.setState({ current })
}
render() {
return (
<div>
<div>
Pick component to render
<button
type="button"
data-trigger="0"
onClick={this.handleChange}
>
Render 1
</button>
<button
type="button"
data-trigger="1"
onClick={this.handleChange}
>
Render 2
</button>
<button
type="button"
data-trigger="2"
onClick={this.handleChange}
>
Render 3
</button>
</div>
{this.renderMyComponent()}
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Reviewing your code:
You don't need constructor here.
...
constructor(props){
super(props);
}
...

Related

Render unique divs for each hovered element

minimum reproducible example: https://codesandbox.io/s/react-hover-example-tu1eu?file=/index.js
I currently have a new element being rendered when either of 2 other elements are hovered over. But i would like to render different things based upon which element is hovered.
In the example below and in the codepen, there are 2 hoverable divs that are rendered; when they are hovered over, it changes the state and another div is rendered. I would like for the HoverMe2 div to render text "hello2". Currently, whether i hover hoverme1 or 2, they both just render the text "hello".
import React, { Component } from "react";
import { render } from "react-dom";
class HoverExample extends Component {
constructor(props) {
super(props);
this.handleMouseHover = this.handleMouseHover.bind(this);
this.state = {
isHovering: false
};
}
handleMouseHover() {
this.setState(this.toggleHoverState);
}
toggleHoverState(state) {
return {
isHovering: !state.isHovering
};
}
render() {
return (
<div>
<div
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
Hover Me
</div>
<div
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
Hover Me2
</div>
{this.state.isHovering && <div>hello</div>}
</div>
);
}
}
render(<HoverExample />, document.getElementById("root"));
You need to keep the state of item which you have hovered that's for sure
const { Component, useState, useEffect } = React;
class HoverExample extends Component {
constructor(props) {
super(props);
this.handleMouseHover = this.handleMouseHover.bind(this);
this.state = {
isHovering: false,
values: ['hello', 'hello2'],
value: 'hello'
};
}
handleMouseHover({target: {dataset: {id}}}) {
this.setState(state => {
return {
...state,
isHovering: !state.isHovering,
value: state.values[id]
};
});
}
render() {
return (
<div>
<div
data-id="0"
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
Hover Me
</div>
<div
data-id="1"
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
Hover Me2
</div>
{this.state.isHovering && <div>{this.state.value}</div>}
</div>
);
}
}
ReactDOM.render(
<HoverExample />,
document.getElementById('root')
);
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<div id="root"></div>
You can pass the context text as shown in example. This is working code:
import React, { Component } from "react";
import { render } from "react-dom";
// Drive this using some configuration. You can set based on your requirement.
export const HOVER_Hello1 = "Hello1";
export const HOVER_Hello2 = "Hello2";
class HoverExample extends Component {
constructor(props) {
super(props);
this.handleMouseHover = this.handleMouseHover.bind(this);
this.state = {
isHovering: false,
contextText: ""
};
}
handleMouseHover = (e, currentText) => {
this.setState({
isHovering: !this.state.isHovering,
contextText: currentText
});
}
toggleHoverState(state) {
//
}
render() {
return (
<div>
<div
onMouseEnter={e => this.handleMouseHover(e, HOVER_Hello1)}
onMouseLeave={e => this.handleMouseHover(e, HOVER_Hello1)}
>
Hover Me
</div>
<div
onMouseEnter={e => this.handleMouseHover(e, HOVER_Hello2)}
onMouseLeave={e => this.handleMouseHover(e, HOVER_Hello2)}
>
Hover Me2
</div>
{this.state.isHovering && <div>{this.state.contextText}</div>}
</div>
);
}
}
export default HoverExample;
If the whole point is about linking dynamically messages to JSX-element you're hovering, you may store that binding (e.g. within an object).
Upon rendering, you simply pass some anchor (e.g. id property of corresponding object) within a custom attribute (data-*), so that later on you may retrieve that, look up for the matching object, put linked message into state and render the message.
Following is a quick demo:
const { Component } = React,
{ render } = ReactDOM,
rootNode = document.getElementById('root')
const data = [
{id:0, text: 'Hover me', message: 'Thanks for hovering'},
{id:1, text: 'Hover me too', message: 'Great job'}
]
class HoverableDivs extends Component {
state = {
messageToShow: null
}
enterHandler = ({target:{dataset:{id:recordId}}}) => {
const {message} = this.props.data.find(({id}) => id == recordId)
this.setState({messageToShow: message})
}
leaveHandler = () => this.setState({messageToShow: null})
render(){
return (
<div>
{
this.props.data.map(({text,id}) => (
<div
key={id}
data-id={id}
onMouseEnter={this.enterHandler}
onMouseLeave={this.leaveHandler}
>
{text}
</div>
))
}
{
this.state.messageToShow && <div>{this.state.messageToShow}</div>
}
</div>
)
}
}
render (
<HoverableDivs {...{data}} />,
rootNode
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
As #CevaComic pointed out, you can do this with CSS. But if you want to use React, for example, because your actual problem is more complex, here is the answer.
You will need a way to tell apart the two elements. It could be done with some neat tricks, like setting an unique id to each element, passing a custom argument, or something else.
But I would advise against "cool tricks" as it's more difficult to understand what is going on, and the code is more prone to errors. I think the best way it to use a dumb approach of unique functions for unique elements.
Each onMouseEnter and onMouseLeave has to be an unique function (e.g. handleMouseHover1 and handleMouseHover2), and each of those functions need to control unique state (for example, isHovering1 and isHovering2). Then you have to render the element you want based on the state. Of course, for a real-world code, you will probably want to use more descriptive names to make the code more comprehensible. The full code would look something like this.
class HoverExample extends Component {
state = {
isHovering1: false,
isHovering2: false
};
handleMouseHover1 = () => {
this.setState(({ isHovering1 }) => ({ isHovering1: !isHovering1 }));
};
handleMouseHover2 = () => {
this.setState(({ isHovering2 }) => ({ isHovering2: !isHovering2 }));
};
render() {
const { isHovering1, isHovering2 } = this.state;
return (
<div>
<div
onMouseEnter={this.handleMouseHover1}
onMouseLeave={this.handleMouseHover1}
>
Hover Me1
</div>
<div
onMouseEnter={this.handleMouseHover2}
onMouseLeave={this.handleMouseHover2}
>
Hover Me2
</div>
{isHovering1 && <div>hello1</div>}
{isHovering2 && <div>hello2</div>}
</div>
);
}
}
Also, updated example: https://codesandbox.io/s/react-hover-example-rc3h0
Note: I have also edited the code to add some syntax sugar which exists with newer ECMAScript versions. Instead of binding the function, you can use the arrow function format, e.g. fn = () => { ... }. The arrow function means the this context is automatically bound to the function, so you don't have to do it manually. Also, you don't have to initialize this.state inside the constructor, you can define it as a class instance property. With those two things together, you do not need the constructor at all, and it makes the code a bit cleaner.

unable to find a React.Component by id

I have a React.Component with render() declared this way:
render(){
return <div>
<button id="butt" onClick={()=> $("#noti").change("test") }>click me</button>
<Notification id="noti" onMounted={() => console.log("test")}/>
</div>
}
And this is my Notification class:
class Notification extends React.Component {
constructor(props) {
super(props)
this.state = {
message: "place holder",
visible: false
}
}
show(message, duration){
console.log("show")
this.setState({visible: true, message})
setTimeout(() => {
this.setState({visible: false})
}, duration)
}
change(message){
this.setState({message})
}
render() {
const {visible, message} = this.state
return <div>
{visible ? message : ""}
</div>
}
}
As the class name suggests, I am trying to create a simple notification with message. And I want to simply display the notification by calling noti.show(message, duration).
However, when I try to find noti by doing window.noti, $("#noti") and document.findElementById("noti"), they all give me undefined, while noti is displayed properly. And I can find the butt using the code to find noti.
How should I find the noti? I am new to front end so please be a little bit more specific on explaining.
It's not a good idea using JQuery library with Reactjs. instead you can find a appropriate react library for notification or anything else.
Also In React we use ref to to access DOM nodes.
Something like this:
constructor(props) {
super(props);
this.noti = React.createRef();
}
...
<Notification ref={this.noti} onMounted={() => console.log("test")}/>
more info: https://reactjs.org/docs/refs-and-the-dom.html
I have hardcoded the id to 'noti' in the render method. You can also use the prop id in the Notification component.I have remodelled the component so that you can achieve the intended functionality through React way.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
messageContent: 'placeholder'
}
}
setMessage = (data) => {
this.setState({messageContent : data});
}
render() {
return (
<div className="App">
<button id='butt' onClick= {() => this.setMessage('test')} />
<Notification message = {this.state.messageContent} />
</div>
);
}
}
class Notification extends React.Component {
render () {
const {message} = this.props;
return (
<div id='noti'>
{message}
</div>
)
}
}
Before beginning: Using id/class to reach DOM nodes is not suggested in React.js, you need to use Ref's. Read more at here.
In your first render method, you give id property to Notification component.
In react.js,
if you pass a property to some component, it becomes a props of that
component. (read more here)
After you give the id to Notification, you need to take and use that specific props in your Notification component.
You see that you inserted a code line super(props) in constructor of Notification? That means, take all the props from super (upper) class and inherit them in this class.
Since id is HTML tag, you can use it like:
class Notification extends React.Component {
constructor(props) {
// inherit all props from upper class
super(props);
this.state = {
message: "place holder",
visible: false,
// you can reach all props with using this.props
// we took id props and assign it to some property in component state
id: this.props.id
}
}
show(message, duration){
// code..
}
change(message){
// code..
}
render() {
const {visible, message, id} = this.state
// give that id to div tag
return <div id={id}>
{message}
</div>
}
}
You can't pass id/class to a React Component as you would declare them in your normal HTML. any property when passed to a React Component becomes a props of that component which you have to use in the component class/function.
render() {
const {visible, message} = this.state
// give your id props to div tag as id attr
return <div id={this.props.id}>
{message}
</div>
}
This answer does not provide the exact answer about selecting a component as you want. I'm providing this answer so you can see other alternatives (more React way maybe) and improve it according to your needs.
class App extends React.Component {
state = {
isNotiVisible: false
};
handleClick = () => this.setState({ isNotiVisible: true });
render() {
return (
<div>
<button onClick={this.handleClick}>Show Noti</button>
{this.state.isNotiVisible && (
<Noti duration={2000} message="This is a simple notification." />
)}
</div>
);
}
}
class Noti extends React.Component {
state = {
visible: true
};
componentDidMount() {
setTimeout(() => this.setState({ visible: false }), this.props.duration);
}
render() {
return this.state.visible && <div>{this.props.message}</div>;
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<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>
<div id="root" />

How do you change what jsx blocks are being rendered from inside a same class?

I'm still learning the basics of React, and I wanted to do something that caught my attention. It's about rendering two things. Is it possible or even plausible to just change what is rendered with a function, and then calling the functions separately with a button or timer?
This is a sample code, to show how it would render two completely different things. It is supposed to render a button that says "State: On" or "State: Off". And when you click the button the state changes. But also, the whole render method is switched... or at least that's what's supposed to be happening.
class Flicker{
constructor(props){
super(props);
this.state = {mode: "on"};
}
flipOn(){
this.setState({mode: "on"})
}
flipOff(){
this.setState({mode: "off"})
}
if (this.state.mode == "on"){
render() {
return(
<button onClick={this.flipOn}>State: On</button>
);
}
} else if (this.state.mode == "off"){
render() {
return(
<button onClick={this.flipOff}>State: Off</button>
);
}
}
}
export default Flicker;
If this isn't the correct way to do this type of changes in what jsx gets rendered on the app, how should it be done?
You have the right idea - but in a class there is only one render method. Your logic does belong inside the render. This should do what you're looking for:
class Flicker extends React.Component {
constructor(props){
super(props);
this.state = {mode: "On"};
}
flipOn(){
this.setState({mode: "On"})
}
flipOff(){
this.setState({mode: "Off"})
}
render () {
return (
<button onClick={(this.state.mode === 'Off') ? this.flipOn : this.flipOff}>State: {this.state.mode}</button>
)
}
}
export default Flicker;
Put the conditional logic inside the render() method.
Something like this...
class Example extends React.Component {
// the rest of your code
render() {
const { mode } = this.state;
return(
<div>
{mode ==="on" && <button onClick={this.flipOn}>State: On</button> }
{mode === "off" && <button onClick={this.flipOff}>State: Off</button>}
</div>
)
}
}
Your component changes the state. The state is boolean - either on (true) or not (false). Since you're state is switched between two value, you can use a single method (toggle). Since we check the previous state, it it's better to setState via updater.
You need to bind the method to this, by using bind in the constructor (method 4) or an arrow function via an instance method (method 5).
Now in the (single) render, you only need to change the text according to the state of on:
class Flicker extends React.Component{
state = { on: true };
toggle = () =>
this.setState(({ on }) => ({ on: !on }));
render() {
const text = this.state.on ? 'On' : 'Off';
return (
<button onClick={this.toggle}>State: {text}</button>
);
}
}
ReactDOM.render(
<Flicker />,
demo
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="demo"></div>
Real world example
The button should probably get on and toggle via props. Now they are available outside, and the Flicker's only concern is calling toggle when it's clicked, and changing the text according to on:
const Toggler = ({ on, toggle }) => (
<button onClick={toggle}>
State: {on ? 'On' : 'Off'}
</button>
);
class Flicker extends React.Component {
state = { on: true };
toggle = () =>
this.setState(({ on }) => ({ on: !on }));
render() {
const { on } = this.state;
return (
<div>
<Toggler toggle={this.toggle} on={on} />
{on &&
'I\'m displayed only when I\'m On'
}
</div>
);
}
}
ReactDOM.render(
<Flicker />,
demo
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="demo"></div>

Render Random Item from State React.JS

Given some items stored in state, I want to be able to click a button and display a random item from that array. So far it only works on the first click and then it displays the same one letter after the first click.
What exactly is going on?
import React, { Component } from 'react'
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
notes: ['hey', 'yo', 'sup'],
clicked: false
}
}
handleClick = () => {
this.setState({
clicked: true,
notes: this.state.notes[Math.floor(Math.random() *
this.state.notes.length)]
})
}
render() {
return (
<div className="App">
<button onClick={this.handleClick}>Random Note</button>
<h1>{this.state.clicked ? this.state.notes : ''}</h1>
</div>
)
}
}
Add selected note handling
import React, { Component } from 'react'
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
notes: ['hey', 'yo', 'sup'],
selectedNote: null,
clicked: false
}
}
handleClick = () => {
this.setState({
clicked: true,
selectedNote: this.state.notes[Math.floor(Math.random() *
this.state.notes.length)]
})
}
render() {
return (
<div className="App">
<button onClick={this.handleClick}>Random Note</button>
<h1>{this.state.clicked && this.state.selectedNote}</h1>
</div>
)
}
}
You're overwriting the notes array in state in your handleClick method. Try using a different key (something like activeNote) in handleClick, then use that in your render method rather than this.state.notes.
So I think I solved it.
I ended up adding another state key randomWord and setting it equal to ''.
I then set the state of randomWord to that of this.state.notes when randomized.
Finally, I rendered this.state.randomWord
No clue if that's the correct approach but it's a working solution, lol.

Access a React component method / state from outside the component

I have embedded a reactjs component into an existing HTML page by referencing it's ID like described in React's tutorial:
ReactDOM.render(
<Page />,
document.getElementById('my-react-compnent')
);
Then:
<div id="my-react-compnent></div>
The component is displayed and works as expected.
Now I want to link a button located on that page to my component (to be specific I would like to retrive its state, but for the example even invoking one of its methods would be fine).
In other words - when clicking the outside button, I want to invoke a method from the Page component?
How can I do that?
The Old Recommendation
Assigning the returned value from ReactDOM.render does allow access to the component and it's methods. For example, in a simple app, we might have:
const PageComponent = ReactDOM.render(<Page />, document.getElementById("app"));
which we can then access using PageComponent, and any of its methods can be accessed with PageComponent.METHOD.
However, according to the docs this might be changed or deprecated and is not recommended.
The New Recommendation
The new recommendation is to attach a callback ref to the root element. Using the same example above:
const PageComponent = ReactDOM.render(<Page ref={(pageComponent) => {window.pageComponent = pageComponent}}/>, document.getElementById("app"));
which we can then access using window.pageComponent, and any of its methods can be accessed with window.pageComponent.METHOD.
This also works for child components.
Here's a full example:
class ChildComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 0
}
}
returnCounter = () => {
return this.state.counter;
}
increment = (event) => {
event.stopPropagation();
this.setState(prevState => {
return {
counter: prevState.counter + 1
}
})
}
render() {
return (
<div onClick={this.increment}>
Child Value - {this.state.counter} - Click to increment
</div>
)
}
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 0
}
}
returnCounter = () => {
return this.state.counter;
}
increment = () => {
this.setState(prevState => {
return {
counter: prevState.counter + 1
}
})
}
render() {
return (
<div onClick={this.increment}>
<div>Parent Value - {this.state.counter} - Click to increment</div>
<ChildComponent ref={(childComponent) => {window.childComponent = childComponent}}/>
</div>
)
}
}
ReactDOM.render(<Page ref={(pageComponent) => {window.pageComponent = pageComponent}} />, document.getElementById("app"));
const parentBtn = document.getElementById("parentButton");
parentBtn.addEventListener("click", event => {
alert(window.pageComponent.returnCounter());
});
const childBtn = document.getElementById("childButton");
childBtn.addEventListener("click", event => {
alert(window.childComponent.returnCounter());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
<button id="parentButton">Get Parent State</button>
<button id="childButton">Get Child State</button>

Categories

Resources