How to show external links as popup in react application? - reactjs

In my react application, when I click on an external link suppose http://www.example.com/about, I do not want it to redirect to that website but I want that about page to render as a popup in my react application. How can I approach this?

You might make a wrapper component for the links that would check if the href prop matches your website location or goes to a 3rd party site, and renders either a normal a tag or a modal dialog with an iframe inside.
Eg. Link.js
import React, { Component, Fragment } from 'react'
import Modal from '...' // any of the available modal component for react
export default class Link extends Component {
state = { isOpen: false }
render () {
const url = new URL(this.props.href)
if (url.hostname === WEBSITE_HOSTNAME) return (
<a href={this.props.href}>{this.props.children}</a>
)
return (
<Fragment>
// you could also use an <a> here if you want users to be able to open the link in a new tab with a right click
<button onClick={() => this.setState({ modalOpen: true })}>{this.props.children}</button>
<Modal isOpen={this.state.isOpen}>
<iframe src={this.props.href} />
</Modal>
</Fragment>
)
}
}
Even better, split it into two components as there's no need for regular links to have any state...

Related

Conditionally render a React component depending on a descendant's render

We use Reakit dialogs to prompt users to take an action in our React web app.
On some pages, we have specific text related to the action and would like to render this specific content in the dialog. On all other pages, we want to fall back to generic text.
Our simplified component hierarchy for generic pages looks like:
<BaseLayout>
...
</BaseLayout>
and for a page where we want to show specific text,
<BaseLayout>
...
<SpecificPage/>
...
</BaseLayout>
What we'd like to happen is:
On pages that render the SpecificPage component, the Dialog appears with the specific text
On pages that do not render the SpecificPage component, the Dialog appears with the fallback generic text
Our approach was to have the SpecificPage component render a Dialog with the page-specific text, and the BaseLayout component render a Dialog with the generic fallback text, but this approach isn't ideal -- users see a flash of the BaseLayout dialog before the SpecificPage dialog is rendered. Is there any way to define a single component that is "overridden" by descendants in the component hierarchy, or other way to achieve this conditional rendering?
You can simply check if you're rendering anything as children in the BaseLayout component or not, If not you can fallback to generic text.
Here's an example.
App Component
import React from 'react';
import { BaseLayout } from './BaseLayout';
export function App(props) {
return (
<div className='App'>
<BaseLayout>
<h1>Hello World.</h1>
</BaseLayout>. // Renders hello world
<BaseLayout /> // Render generic text
</div>
);
}
Base Layout Component
import React from 'react';
export function BaseLayout({children}) {
return (
<div>
{children ? children : "Some Generic Text"}
</div>
);
}
See https://github.com/ariakit/ariakit/discussions/1266#discussioncomment-2617748 for a solution and CodeSandbox that solves this problem well using the Constate library.

Button won't open new component

I'm learning React and I'm having issues opening a new component that I created. On my page I have a button to create a new record and when clicked the URL changes but the component doesn't render.
Note: I am able to open existing components within the app, just not newly created new ones.
The code looks like:
<div>
<NavLink className="btn blue" to="./CreateNewUser">Add New User</NavLink>
</div>
the component:
CreateNewUser.js
import React, { Component } from 'react';
export class CreateNewUser extends Component {
render() {
return <h2>Add New User</h2>;
}
}
You are very likely missing the route to your new component in react-router.
You can check the sample code in this link for reference, specifically the <Switch> tag and the <Route> tags within it

Passing props from parent to sibling in React

I am recreating a simple React app that I have already created in Angular. The React app has two components: one (menus.js) for a side menu and a second (content.js) that will display the content from each item in the menu when each link is clicked (just like an iframe of sorts). In the App.js I am making a REST API call to populate the state for the menus.js component. Note that both components are in the App.js as follows:
App.js
import React,{Component} from 'react';
import Menus from './components/menus';
import Content from './components/content';
class App extends Component {
state = {
menus: []
}
componentDidMount(){
fetch('api address')
.then(res => res.json())
.then((data)=> {
this.setState({menus: data})
})
.catch(console.log)
}
render(){
return (
<div>
<div><Menus menus={this.state.menus} /></div>
<div><Content /></div>
</div>
);
}
}
export default App;
here is the menu.js component; it takes a prop (menus) from App.js and builds the menu links with items from it:
import React from 'react';
import { BrowserRouter as Router, Link,} from "react-router-dom";
const Menus = ({ menus }) => {
return (
<Router>
<div>
<center><h1>Lessons</h1></center>
{menus.map(menu => (
<li key={menu.lesson}>
<Link to={`/lesson/${menu.lesson}`}>{menu.lessonName}</Link>
</li>
))}
</div>
</Router>
);
};
export default Menus;
Here is what I need - how do I pass items from the same prop (from App.js) to the content component? FYI - I need this to happen each time a link in the menu in menu.js is clicked (which is why a key is used in the list The simple idea is content will update in the content component each time a menu link in the menu component is clicked.
**content.js**
import React from 'react'
const Content = () => {
return (
<div>{menu.content}</div>
)
};
export default Content
Based on your description of the problem and what I can see of what you've written, it seems to me like you are trying to build an application where the menu persists, but the content changes based on menu clicks. For a simple application, this is how I would structure it differently.
<ParentmostComponent>
<MenuComponent someProp={this.state.some_data} />
<Switch>
<Route path={"/path"} render={(props) => <Dashboard {...props} someProp={this.state.some_other_data_from_parents} />
</Switch>
</ParentMostComponent>
This would allow the menu to always stay there no matter what the content is doing, and you also won't have to pass the menu prop to two components.
In your menu.js, attach the menu object to the Link
...
{menus.map(menu => (
<li key={menu.lesson}>
<Link to={{
pathname: `/lesson/${menu.lesson}`,
state: menu
}}> {menu.lessonName} </Link>
</li>
))}
...
In your content.js receive the menu like this:
import React from 'react'
const Content = () => {
console.log(props.location.state.menu.content);
return (
<div>{props.location.state && props.location.state.menu.content }</div>
)
};
export default Content
Read more here
Your example uses React Router, so this answer uses it as well.
First of all, move the Router up the hierarchy from Menus to App to make the router props available to all components. Then wrap your Content inside a Route to render it conditionally (i.e. if the path matches "/lesson/:lesson"):
class App extends Component {
state = {
menus: [
{
lesson: '61355373',
lessonName: 'Passing props from parent to sibling in React',
content: 'I am recreating a simple React app…'
},
{
lesson: '27991366',
lessonName: 'What is the difference between state and props in React?',
content: 'I was watching a Pluralsight course on React…'
}
]
}
render() {
const { menus } = this.state
return (
<Router>
<div>
<div><Menus menus={menus}/></div>
<Route path="/lesson/:lesson" render={({ match }) => (
<div><Content menu={menus.find(menu => menu.lesson === match.params.lesson)}/></div>
)}>
</Route>
</div>
</Router>
)
}
}
With the help of the render prop, you can access the router props (in this case match.params.lesson) before rendering your child component. We use them to pass the selected menu to Content. Done!
Note: The basic technique (without React Router, Redux etc.) to pass props between siblings is to lift the state up.

My components are not being rendered when I click a link that should load them

I'm confused as to why nothing happens when I'm clicking links in my app.
In my index.js file, I am loading my main screen called 'Game'. Inside 'Game', I have two links, that when clicked, should render another screen.
In my index.js:
import React from "react";
import ReactDOM from "react-dom";
import Game from "./Game/Game";
ReactDOM.render(
<React.Fragment>
<Game/>
</React.Fragment>,
document.getElementById('gameContainer')
)
In my index.html:
<div>
<div id="gameContainer"></div>
</div>
<div id="root"></div>
My Game.js:
import React from "react";
import CharacterStats from "../CharacterStats";
import DungeonStats from "../DungeonStats";
const characterStatsComponent = () => {
return (
<CharacterStats />
);
}
const dungeonStatsComponent = () => {
return (
<DungeonStats />
);
}
const Game = () => (
<div>
<a id="showCharacter" href="#" onClick={characterStatsComponent}>Show your character</a>
</div>
<br />
<div>
<a id="showDungeon" href="#" onClick={dungeonStatsComponent}>Show current dungeon</a>
</div>
);
export default Game;
The two other components, CharacterStats and DungeonStats are just a few bits of html and reactjs to show some data.
Neither CharacterStats or DungeonStats are loading when I'm clicking the links.
I am also getting no errors in the console.
Nothing happens when the links are clicked.
I also put this inside each onClick event:
console.log('link was clicked');
And it does show the message in the console. So that shows that it knows the link is being clicked.
Is there anything that would prevent them from being loaded?
Thanks!
It wont work because you are returning jsx into the onClick function context, and not into the Game component's return value.
You could define a state using useState, something like showDungeon and showCharacter that defaults to false, change it to true onClick, and in the Game component's return value add:
{ showDungeon && <DungeonStats /> }
React uses something called Synthetic Events to achieve cross browser event handling. If I understood your question correctly than changing the onclick to onClick should do the job for you.

React Select inside Semantic UI React

I have a simple React Select as content prop in a Semantic UI React Popover component. I am constrained by each of the package versions inside the project (available in the following codesandbox https://codesandbox.io/s/wy194rz908):
React: ~15.5.0
ReactDOM: ~15.5.0
React-Select: ^2.1.1
Semantic UI React: 0.71.5
As you can see, the React Select options closes when a selection is done.
On the other hand, I found that updating React, React-DOM and SemanticUI to their latest versions make the feature work. As you can see, the selection is done and the Select options do not fold (available in the following codesandbox https://codesandbox.io/s/6y14qyykk3).
As I can not update update the React and SUIR, what workaround should I follow in order to make this work?
Thanks!
You have to use a Controlled Popup Component, as stated in docs:
import React from 'react'
import { Button, Popup } from 'semantic-ui-react'
class PopupExampleContextControlled extends React.Component {
state = {}
toggle = () => this.setState({ open: !this.state.open })
handleRef = node => this.setState({ node })
render() {
const { node, open } = this.state
return (
<div>
<Button content='Open controlled Popup' onClick={this.toggle} />
<Popup context={node} content='Hello' position='top center' open={open} />
---------->
<strong ref={this.handleRef}>here</strong>
</div>
)
}
}
export default PopupExampleContextControlled
In this way you can control when popup opens and closes.
I am controlling the Popup using the open prop available through its props api. I change its state from true to false when clicking the caret down icon button.
Solution: https://codesandbox.io/s/rmoxx98qkn

Resources