Change the page HTML on button click React - javascript

So I have an already written heapsort animation [here][1] with JS/Jquery + HTML+ CSS. We are currently rebuilding +expanding the project in React. I don't know it (or web technologies in general), and so far I've managed by working on the DS while my partner does the react specific things+ animations. I've been given the green light to just use the heap animation since he liked it too. I've been banging my head trying to integrate the HTML file in to react. Trying iframe, window.location, and the other SO things just haven't worked for me. This is why I'm going with the approach of trying to set the HTML code to my HTML file when I click it.
The directory structure is:
heap.js
heapsort (dir) (copied from project):
a)index.html
b)static (dir):
i)styles.css <br>
ii)style.css <br>
iii)heaper.js (does the actual animation work) <br>
I'd like to do something where a button click in heap.js will load up the index.html. If that can load up correctly, everything should work smoothly.
function create(){
//set HTML->./heapsort/index.html
window.location="./heapsort/index.html" //does not work despite going to the correct path
}
Edit: added code
Heap.js (everything except the create function works as is supposed to)
import React, { useState } from 'react';
import { CSSTransition } from 'react-transition-group';
// import { Component } from 'react';
// import Page from './heapsort/index.html';
import VisualPage, {
About,
Complexity,
Controls,
ControlGroup,
Visualization,
} from '../VisualPage.js';
import './Styles/queue.css';
export function Heap(props) {
return (
<div className="heap">
{props.children}
</div>
);
}
export function QueueNode(props) {
return (
<CSSTransition
appear
in={props.show}
onExited={props.onExited}
timeout={200}
unmountOnExit
classNames="queue-node"
>
<div className={"queue-node" + (props.highlight ? " highlight" : "")}>
{props.children}
</div>
</CSSTransition>
);
}
function Demo() {
const [list, setList] = useState([]);
const [length, setLength] = useState(0);
function onExited() {
setList(list.slice(1));
}
function create(){
//set HTML->./heapsort/index.html
// window.location="./heapsort/index.html" //does not work despite going to the correct path
window.location.replace("./heapsort/index.html")
}
return (
<>
<Controls>
<ControlGroup>
<label htmlFor="create">Len of randomized array</label>
<input name="add" type="text" onChange={e => setLength(e.target.value)}></input>
<button onClick={create}>Create Array</button>
</ControlGroup>
</Controls>
<Visualization>
<Heap>
{list.map((node, i) => {
return (
<QueueNode
key={i}
index={i}
show={node.show}
highlight={node.highlight}
onExited={onExited}
>
{node.value}
</QueueNode>
);
})}
</Heap>
</Visualization>
</>
);
}
export default function QueuePage(props) {
return (
<VisualPage title="Array">
<About>
<h4>What is a Heap?</h4>
The bane of my existance.
</About>
<Complexity complexity={[
{
"name": "Indexing",
"complexity": "Θ(1)"
},
{
"name": "Set Element at Index",
"complexity": "Θ(1)"
},
{
"name": "Average wasted space",
"complexity": "Θ(1)",
},
]} />
<Demo />
</VisualPage>
);
}
```<br>
Index.html
-->
<input type="number" id="len" placeholder="Insert length of randomized array"></input>
<button id="click">Generate Arr of Length</button>
<button id="Refresh" onclick="refresh()">Refresh</button>
var myLink = document.getElementById('click');
function refresh(){
location.reload();
}
myLink.onclick = function () {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "./static/heap.js";
document.getElementsByTagName("head")[0].appendChild(script);
return false;
}
```
[1]: https://dl1683.github.io/DataStructuresInJavaScript/ds/heapsort/index.html

window.location doesn't redirect to another page. You must use
window.location.replace()
function create(){
window.location.replace("./heapsort/index.html")
}

Related

Why does my TOC component add `{"-" + 1}` to its links every time I click on one of them?

I implemented a table-of-contents component to my site following this tutorial.
It works, but every time I click on one of the TOC's links, all the links (including the clicked one) in the TOC get a {"-" + 1} appended. So if I click a link, all those links go from #first-heading, #second-heading, etc. to #first-heading-1, #second-heading-1, etc. If I click one of the links again, they will all get #first-heading-2, #second-heading-2 etc., and so on. This behavior is of course problematic, as it breaks the links.
What's causing this? How do I fix it?
I noticed the tutorial uses the remark-slug plugin for the headings, while I use the gatsby-autolink-headers plugin. Can that be the source of the problem? I've not been able to test with the former, as I get an error when trying to install it.
EDIT: I've tried with both plugins. Same problem.
TableOfContents.js
import React from "react"
import Slugger from "github-slugger"
import { Link } from "gatsby"
const slugger = new Slugger()
export default ({ headings }) => (
<div className="table-of-contents">
<h3>On this page</h3>
<ol>
{headings
.filter(heading => heading.depth !== 1)
.map(heading => (
<li key={heading.value}>
<Link
to={"#" + slugger.slug(heading.value)}
>
{heading.value}
</Link>
</li>
))}
</ol>
</div>
)
post-template.js
import * as React from "react"
import { graphql } from "gatsby"
import { MDXRenderer } from "gatsby-plugin-mdx"
import Layout from "../components/layout.js"
import Seo from "../components/seo.js"
const PostTemplate = ({ data, location }) => {
let post = data.mdx
return (
<Layout location={location}>
<Seo
title={post.frontmatter.title}
description={post.frontmatter.lead}
date={post.frontmatter.computerDate}
/>
<article className="article">
<h1 itemprop="headline">{post.frontmatter.title}</h1>
<p
className="lead"
itemprop="introduction"
>
{post.frontmatter.lead}
</p>
<MDXRenderer headings={post.headings}>
{post.body}
</MDXRenderer>
</article>
</Layout>
)
}
export default PostTemplate
export const pageQuery = graphql`
query PostBySlug($id: String!) {
site {
siteMetadata {
title
}
}
mdx(id: {eq: $id}) {
id
excerpt(pruneLength: 160)
body
frontmatter {
title
computerDate: date(formatString: "YYYY-MM-DD")
humanDate: date(formatString: "DD MMMM YYYY")
lead
}
headings {
depth
value
}
}
}
`
index.mdx
---
/* frontmatter */
---
<!-- component imported as shortcode in `layout.js` -->
<TableOfContents headings={props.headings} />
layout.js (excerpt)
import TableOfContents from "./article-components/TableOfContents"
const shortcodes = {
TableOfContents
}
export default function Layout({ children }) {
return (
<div className="layout-wrapper">
<Header />
<main>
<MDXProvider components={shortcodes}>
{children}
</MDXProvider>
</main>
</div>
)
}
It's because of the slugger. In their docs:
slugger.slug('foo')
// returns 'foo'
slugger.slug('foo')
// returns 'foo-1'
slugger.slug('bar')
// returns 'bar'
slugger.slug('foo')
// returns 'foo-2'
Because it ensures that the links are unique (like GitHub does), it appends the -1, -2, etc.
As long as you use you gatsby-autolink-headers plugin can get rid of the slugger implementation. If you need, you can use the normal link value (heading.value), the slug field (if provided), or sanitize it using a custom function like:
function slugify (text) {
return text
.toString()
.toLowerCase()
.normalize(`NFD`)
.trim()
.replace(/\s+/g, `-`)
.replace(/[^\w-]+/g, ``)
.replace(/--+/g, `-`);
};
<Link to={"#" + slugify(heading.value)}>

React: implementing a router

I tried implementing browser router, but to no success. i'm having trouble with useParams hook, and just the router in general. Looked through multiple posts and i just wasn't able to get it working. I'll post the most barebones code below, hoping someone knows the solution. I removed the traces of the router, since it didn't work.
App.js is currently empty:
const App=()=> {
return (
<Main/>
);
}
Main.jsx is my main element, where components change. There isn't a page change per se, everything is in the main element. values get passed through props into main and written into state, so the useEffect can change visibility of components based on what you chose, first category, then recipe.:
const Main =()=> {
const [showElement, setShowElement] = useState("category");
const [selectedCategory, setSelectedCategory] = useState();
const [selectedRecipe, setSelectedRecipe] = useState();
useEffect(()=> {
if (selectedRecipe) {
setShowElement("recipe")
} else if (selectedCategory) {
setShowElement("recipeSelection")
}
window.scrollTo(0, 0)
}, [selectedCategory][selectedRecipe]);
return (
<>
<Header />
<main className="main">
<div>
<div>
{showElement === "category" &&
<CategoryWindow
passSelectedCategory={setSelectedCategory}
/>
}
</div>
<div>
{showElement === "recipeSelection" &&
<RecipeSelection
value={selectedCategory}
passSelectedRecipe={setSelectedRecipe}
/>
}
</div>
<div>
{showElement === "recipe" &&
<RecipeWindow
value={selectedRecipe}
/>
}
</div>
</div>
</main>
</>
)
}
This is the recipe picker component. For example when i click on curry, i'd like the url to show /food/curry. None od the names are hardcoded, everything comes from a javascript object:
const RecipeSelection =(props)=> {
const recipies = Recipies.filter(x=>x.type === props.value);
return (
<div className="selection-div">
<div className="selection-inner">
{recipies.map(selection =>
<>
<img src={require(`../images/${selection.id}.png`)}
className="selection-single"
key={selection.id}
alt={"picture of " + selection.id}
onClick={()=> props.passSelectedRecipe(selection.id)}
>
</img>
<div className="container-h3"
onClick={()=> props.passSelectedRecipe(selection.id)}
>
<h3 className="selection-h3">{selection.name}</h3>
</div>
</>
)}
</div>
</div>
)
}

Using i18next to replace a variable with a ReactNode element

I have a translation json file with the following translation:
"pageNotFound": {
"description": "The page could not be found. Click {{link}} to return to the home page"
},
The link variable I am wanting to be replaced by a ReactRouter <Link>
I have the following code in my render method which outputs the below picture.
public render() {
const { t } = this.props;
const message = t('pageNotFound.description', { link: <Link to="/">here</Link> });
return (
<div className="body-content">
<div>
{message}
</div>
</div>
);
}
I have played with the <Trans> component and I think this may be a way but it seems like you have to type the full text including <> tags which for my use case is not what i'm after as I want all text to be in the translation json if possible.
Any recommendations are welcome
You should use Trans component for this.
"pageNotFound": {
"description": "The page could not be found. Click <0>here</0> to return to the home page"
},
public render() {
const { t } = this.props;
return (
<div className="body-content">
<div>
<Trans
t={t}
i18nKey="pageNotFound.description"
components={[
<Link key={0} to="/">
here
</Link>,
]}
/>
</div>
</div>
);
}

Returning multiple HTML snippets per element of array

Edited --
Let's say I have an array of JSON objects:
"social":[
{
"name":"facebook",
"className":"fa fa-facebook"
},
{
"name":"linkedin",
"className":"fa fa-linkedin"
},
{
"name":"instagram",
"className":"fa fa-instagram"
},
{
"name":"github",
"className":"fa fa-github"
}
]
How do I create an snippet for each of the objects such that they return
<p>{social.name}<p>
And I don't want to use map.
This is generalized for a more complicated example, but this seems to be the problem I am facing (i.e. I have the data in the format below and I need to get the property from each of the elements to display and I only have one function)
Assuming that social is a part of the state, you can implement a method that maps each item in the social array to a p tag:
renderSocialNames = () => {
return this.state.social.map(
socialItem => <p key={socialItem.className}>{socialItem.name}</p>
);
}
Here's a Working Sample StackBlitz for your ref.
cleaner code :) , this might solve your issue
import React, { Component } from "react";
class Projects extends Component {
constructor(props) {
//initialize this component with props
super(props);
}
render() {
const { data } = this.props;
if (data) {
const projects = data.map(project => {
return (
<a className="cell" data-remodal-target={project.id}>
<img
className="grid-image"
src={project.cover}
data-aload={projects.cover}
alt={project.name}
/>
</a>
);
});
const modals = data.map(project => {
return (
<div className="remodal" data-remodal-id={project.id}>
<button
data-remodal-action="close"
className="remodal-close"
></button>
<h1>Remodal</h1>
<p>
Responsive, lightweight, fast, synchronized with CSS animations,
fully customizable modal window plugin with declarative
configuration and hash tracking.
</p>
<br />
<button data-remodal-action="cancel" className="remodal-cancel">
Cancel
</button>
<button data-remodal-action="confirm" className="remodal-confirm">
OK
</button>
</div>
);
});
}
return (
<section id="projects">
<div className="grid-container remodal-bg">
{projects}
{modals}
</div>
</section>
);
}
}

React not updating text (issue with .getDOMNode())

I'm using React to make a Markdown previewer and trying to make sense of what was used here to make the right box update with a live preview as soon as the text is changed on the left. They have this code at the bottom:
var RawInput = React.createClass({
update:function(){
var modifiedValue=this.refs.inputValue.getDOMNode().value;
this.props.updateValue(modifiedValue);
},
render:function(){
return (<textarea rows="22" type="text" ref="inputValue" value={this.props.value} onChange={this.update} className="form-control" />)
}
});
I implementing this in my code:
const InputText = React.createClass ({
update() {
let newValue = this.refs.inputValue.getDOMNode().value;
this.props.updateValue(newValue);
},
render() {
return (
<div>
<textarea
id="input-text"
rows="18"
type="text"
ref="inputValue"
value={this.props.value}
onChange={this.update}
/>
</div>
);
}
});
The app runs fine except that there is no live preview and the text on the right doesn't update. In the console I get this error: this.refs.inputValue.getDOMNode is not a function .
Here is the full code:
import React from 'react';
import Banner from './components/Banner.jsx';
import ContainerHeader from './components/ContainerHeader.jsx';
import marked from 'marked';
const App = React.createClass ({
updateValue(newValue) {
this.setState ({
value: newValue
})
},
getInitialState() {
return {
value: 'Heading\n=======\n\nSub-heading\n-----------\n \n### Another deeper heading\n \nParagraphs are separated\nby a blank line.\n\nLeave 2 spaces at the end of a line to do a \nline break\n\nText attributes *italic*, **bold**, \n`monospace`, ~~strikethrough~~ .\n\nShopping list:\n\n * apples\n * oranges\n * pears\n\nNumbered list:\n\n 1. apples\n 2. oranges\n 3. pears\n'
}
},
markup(value) {
return {__html: value}
},
render() {
return (
<div>
<Banner />
<div className="container">
<div className="row">
<div className="col-xs-12 col-sm-6">
<ContainerHeader text="I N P U T" />
<InputText
value={this.state.value}
updateValue={this.updateValue} />
</div>
<div className="col-xs-12 col-sm-6">
<ContainerHeader text="O U T P U T" />
<div id="output-text">
<span dangerouslySetInnerHTML={this.markup(marked(this.state.value))}></span>
</div>
</div>
</div>
</div>
</div>
);
}
});
const InputText = React.createClass ({
update() {
let newValue = this.refs.inputValue.getDOMNode().value;
this.props.updateValue(newValue);
},
render() {
return (
<div>
<textarea
id="input-text"
rows="18"
type="text"
ref="inputValue"
value={this.props.value}
onChange={this.update}
/>
</div>
);
}
});
export default App;
Any help welcome on solving this, and thanks!
Since version 15.* in React this.refs.inputValue refers to DOMElement, so you don't need use getDOMNode;
this.refs.inputValue.value
However, I think in this case you don't need use refs, as you call update inside onChange event, you can get target(refer to textarea) from event object, and from target get value
update(e) {
this.props.updateValue(e.target.value);
},
The example is using React v0.14.x which was one combined module.
From React v15 (0.15, but they changed to use it as the major) the methods and classes were split into two modules, React and ReactDOM.
You need to import and use ReactDOM.findDOMNode(component), documentation for which can be found here.
You actually do not need this.refs.inputValue
update(e) {
this.props.updateValue(e.target.value);
},

Categories

Resources