Im beginner on Reactjs and trying to learn and improve, here i have code where is < h1 >test< / h1 > and under this should appear numbers under each other like this 1:1 1:2 1:3, but css does not seem to work with it, i get numbers but without css and i dont get any error message either... is here something wrong ? the code :
import React, { Component } from 'react'
class Button extends Component {
state = {}
button = () => {
const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "http://*****.*****.com/numbers.txt";
fetch(proxyurl + url)
.then(response => response.text())
.then(contents => document.write(contents))
}
render() {
return (
<div >
<h1>test</h1>
<div style={{ color: 'red' }}>{this.button()}
</div>
</div >
);
}
}
export default Button;
css:
body {
background: url('***.png');
color:red;
margin:50px 0;
padding:0px;
text-align:center;
}
#root {
white-space: pre;
}
Your render function should be pure, see https://reactjs.org/docs/react-component.html#render:
The render() function should be pure, meaning that it does not modify component state, it returns the same result each time it’s invoked, and it does not directly interact with the browser.
Your render function contains a call to this.button. So every time your component re-renders, a fetch request is made when it seems this should only be called once. As the docs suggest, move this logic into componentDidMount.
Now, onto your actual problem. You are calling document.write, and it seems you don't understand how this works. Document.write will remove all event listeners from the page and replace all content within body with the argument you supplied. Assuming you had a root element with an ID of root (<div id="root">...</div>), that will have been removed after your call to document.write; so your CSS #root selector will no longer point to an existing element.
Instead of using document.write, set the content on your component's state and render that:
import React, { Component } from "react";
export default class Button extends Component {
state = {
contents: null
};
componentDidMount() {
const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "http://*****.*****.com/numbers.txt";
fetch(proxyurl + url)
.then(response => response.text())
.then(contents => this.setState({ contents }));
}
render() {
return (
<div>
<h1>test</h1>
<div style={{ whiteSpace: "pre" }}>{this.state.contents}</div>
</div>
);
}
}
If you're using React, you should have no reason to call document.write, even if you're doing it for testing or you're trying to implement some sort of page reload / turbolinks feature–there are much better alternatives.
Related
What if we forget put the parentheses after calling function, like when we have
renderContent(){}
and we want to call it in a div
<div>{this.renderContent()}</div>
what if we forget write ()?
it shows no error and shows nothing in the screen
and when we put () after calling, it shows if I got a problem like this how can I know where I didn't put ()
if you need more information please let me know
All that will happen in this specific case is it will print the function's code. So for example if you have a function like
renderContent = () => {
console.log("hi");
return <p>Hello world</p>;
}
Then using it without the parenthesis will just print the contents of the function, whereas using the parenthesis will print the JSX for the p tag <div>{this.renderContent()}</div>
will return <p>Hello world</p>
within the div, whereas without the parenthesis, it will just print
() => {
console.log("hi");
return <p>Hello world</p>;
}
which is the function's contents
import React, { Component } from 'react';
class Content extends Component {
renderContent = () => <h1>Hi</h1>
render() {
return (
<div>{this.renderContent()}</div>
)
}
}
export default Content;
This will show "Hi" in the screen.
But if you've deleted () from this.renderContent():
import React, { Component } from 'react';
class Content extends Component {
renderContent = () => <h1>Hi</h1>
render() {
return (
<div>{this.renderContent}</div>
)
}
}
export default Content;
It's showing nothing on the screen, and you would have an error in the console:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
Because it supposed to return the structure of the function (if in vanilla javascript), but this is not allowed in React js
Update: I added a codesanbox at the bottom of this with a more detailed explanation of what I need done.
So I don't quite understand how classes work in React, I am new to react and have used useState a little bit, but never have know what to do with a class. I am using this react package that has an example of how to use a controller to make you animation interactive built with flare now called rive. https://github.com/2d-inc/Flare-React#readme
What I want to achieve is to either run a different animation, or the same animation again when I hover over canvas element that is generated. I can create a second animation in flate(rive) that would still output in the same .flr file and I should then be able to reference it in the controller and run it on hover, just stuck on how to even do that part, or even get this controller to work. One thing to note is I can get the animation to run fine without the controller.
In the docs they have this example code
class PenguinController extends FlareComponent.Controller
{
constructor()
{
super();
this._MusicWalk = null;
this._Walk = null;
this._WalkTime = 0;
}
initialize(artboard)
{
this._MusicWalk = artboard.getAnimation("music_walk");
this._Walk = artboard.getAnimation("walk");
}
advance(artboard, elapsed)
{
// advance the walk time
this._WalkTime += elapsed;
const { _MusicWalk: musicWalk, _Walk: walk, _WalkTime: walkTime } = this;
// mix the two animations together by applying one and then the other (note that order matters).
walk.apply(walkTime % walk.duration, artboard, 1.0);
// if you want to slowly disable the head bobbing (musicWalk animation) you could ramp down the
// final argument (the mix argument) to 0.0 over some time. For now we're mixing at full strength.
musicWalk.apply(walkTime % musicWalk.duration, artboard, 1.0);
// keep rendering
return true;
}
}
First of all what is a constructor? what does super mean? then what are they defining in the constructor, is that some state, how do I determine what to define here?
For the initialized I assume I match it to the state above, and get the animation by the name I named it in flare(rive)
The advance part I don't really understand are we setting the animation with this._WalkTime += elapsed; to how long the animation runs? I think I understand the apply section, it is applying a duration to the animation.
Next it has this code
class MyComponent extends React.Component
{
constructor()
{
this.state = { penguinController: new PenguinController() };
}
render()
{
return <FlareComponent controller={this.state.penguinController} /*... more properties here ...*/ />;
}
}
Here is my attempted code currently I get the following error
TypeError: Cannot set property 'state' of undefined
import React from "react"
import PropTypes from "prop-types"
import { useStaticQuery, graphql } from "gatsby"
import FlareComponent from 'flare-react';
import styled from 'styled-components'
import Header from "./header"
import "../sass/index.scss"
const LogoWrapper = styled.div`
width:200px;
height:200px;
`
class AnimationController extends FlareComponent.Controller
{
constructor()
{
super();
this._MusicWalk = null;
}
initialize(artboard)
{
this._MusicWalk = artboard.getAnimation("Wispy Appear");
}
advance(artboard, elapsed)
{
const { _MusicWalk: musicWalk } = this;
musicWalk.apply(musicWalk.duration, artboard, 1.0);
// keep rendering
return true;
}
}
const Layout = ({ children }) => {
const data = useStaticQuery(graphql`
query SiteTitleQuery {
site {
siteMetadata {
title
}
}
}
`)
this.state = { AnimationController: new AnimationController() };
return (
<>
<LogoWrapper>
<FlareComponent width={200} height={200} animationName="Wispy Appear" controller={this.state.AnimationController} file="/wispy-2.flr"/>
</LogoWrapper>
<main className="main-wrapper">{children}</main>
<footer>
© {new Date().getFullYear()}, Built with
{` `}
Gatsby
</footer>
</>
)
}
Layout.propTypes = {
children: PropTypes.node.isRequired,
}
export default Layout
For reference there is a a tutorial on how to make an animation interactive but it's for flutter, but it has some insights into there api https://medium.com/rive/building-a-responsive-house-with-flare-flutter-31af823ba805
Update
Here is my new attempted code after trying to read up on classes in es6, I still need to learn more.
So how do I go about running a second animation on click or hover or any event. The animation runs once now, but I don't know whow to use the controller?
import React from "react"
import Img from 'gatsby-image'
import styled from "styled-components"
import FlareComponent from 'flare-react';
import Layout from "../components/layout"
const LogoWrapper = styled.div`
width:200px;
height:200px;
`
class wispyController extends FlareComponent.Controller
{
constructor()
{
super();
this._Animate = null;
}
initialize(artboard)
{
this._Animate = artboard.getAnimation("Wispy Appear");
}
advance(artboard, elapsed)
{
const { _Animate: animate } = this;
animate.apply(5, artboard, 1.0);
// keep rendering
return true;
}
}
class IndexPage extends React.Component {
constructor()
{
super();
this.state = { wispyController: new wispyController() };
}
render(){
const {data} = this.props;
return(
<Layout>
<LogoWrapper>
<FlareComponent width={200} height={200} animationName="Wispy Appear" controller={this.state.wispyController} file="/Wispy.flr"/>
</LogoWrapper>
{data.allSanityBlogPost.edges.map(({ node: post }) => (
<article key={post.id}>
<h1>{post.name}</h1>
<img src={post.imageGif.asset.url}/>
</article>
))}
</Layout>
)
}
}
export default IndexPage
export const query = graphql`
query IndexQuery {
allSanityBlogPost {
edges {
node {
name
id
imageGif {
asset {
url
}
}
}
}
}
}
`
Ok Hopefully someone can help here, I made a codesandbox so someone can see what I am trying to achieve. There are two animations, on the page you can see the first one which has a controller which should be mixing the two, and then the other two animations on there own. What I want to happen is the first animation run and then the second one to run on hover. HEre is the code sandbox https://codesandbox.io/s/frosty-water-jnj9m
Classes in the context of React can be thought of as classes in most Object Oriented Programming language, they are a blueprint to create an object from. In order to let the language know how and what to do when we create it, it needs a constructor method. This constructor method is calling a special method called super() so that it calls the constructor of the class it is extending from, in this case FlareComponent.Controller
The method advance will be called to add to the captured walk time that the class is keepi track of.
One of the problems is that you are trying to set the state of the component directly instead of using setState https://reactjs.org/docs/react-component.html
I would highly recommend brushing up on React basics before you continue with this, it will really help you get the proper foundation you need.
I'm beginner on React, and trying to learn and improve, here i have a button which is needed to be clicked and after that should appear bunch of numbers like this 1:1 1:2 1:3, but here i seem to have a problem, my button does not appear, just numbers from URL appears, and i also have css to my button which is not working either when refreshing the page css works just for 1 second then disappear and im not getting any error message...
Here is the code:
import React, { Component } from 'react'
class Button extends Component {
componentWillMount() {
var proxyurl = "https://cors-anywhere.herokuapp.com/";
var url = "http://*****.******.com/numbers.txt";
fetch(proxyurl + url) // https://cors-anywhere.herokuapp.com/https://example.com
.then(response => response.text())
.then(contents => document.write(contents))
}
render() {
return (
<div className="whole">
<button onClick={this.componentWillMount} >Increment</button>
</div>
)
}
}
export default Button;
.whole {
background-color: blue;
color: red;
margin: 50px 0;
padding: 0px;
text-align: center;
}
#root {
white-space: pre;
}
english is not my mother language so sorry for mistakes.
componentWillMount is a lifecycle event of the component and shouldn't be used like this (also it is recommended that componentWillMount is no longer used).
If you want the button to retrieve data you need to create a new function and assign it to the onClick prop of the button. eg:
handleClick = () => {
var proxyurl = "https://cors-anywhere.herokuapp.com/";
// more code
.then(contents => this.setState({ responseText: contents }))
}
render() {
return (
<div className="whole">
<button onClick={this.handleClick}>Increment</button>
</div>
<div>{this.state.responseText}</div>
);
}
ImagesUpload.jsx --> the Presentational component
deleteImageWarning.jsx --> the Notifications component
index.js --> where I exported the deleteImageWarning function
The goal
I want to include a notification or popup in my React app that alerts the user and gives them the choice to either cancel or confirm an action, in this case, deleting an image attached to a job sheet. This notification should be triggered when the user clicks the Delete button located next to the image that has been attached to the page.
Where to look for the issue
What I wrote (please have a look below) is not working whatsoever. I feel there is something wrong with the validateBeforeDelete function; I just wanted to have something that returns the notification function with the right values in the DOM. In addition, I am missing what to write in the Content section in the deleteImageWarning component.
Brief overview
To give you an idea, the button's delete functionality was working perfectly fine prior to working on the notification. There is a container for the ImagesUpload file, therefore, we could state that the ImagesUpload.jsx file is the Presentational Component and there is a ImagesUploadContainer.jsx file that acts as the Container Component for the Presentational Component.
The issue
The problem is that I don't know how to pass the delete function that I declared in the ImagesUpload.jsx file to the deleteImageWarning.jsx component. And that's surely what I am missing in the Content constant of my deleteImageWarning component. Does it have anything to do with the constants declared in my render() function?
ImagesUpload.jsx
//importing the deleteImageWarning function
import {
deleteImageWarning,
} from '../common/notifications';
//this is the function that deletes the image with the required values
async handleDeleteImage (jobsheetId, imageId) {
this.props.deleteImage({jobsheetId: jobsheetId, imageId: imageId});
}
//this is a validate function that is supposed to trigger the deleteImageWarning function
validateBeforeDelete = (jobsheetId, imageId) => {
return deleteImageWarning(this.notificationDOMRef.current, () => this.handleDeleteImage(jobsheetId, imageId));
}
render() {
const { ... } = this.props;
const { ... } = this.state;
return (
//TO BE AWARE! the following delete button with an onClick function has been written using React final form's syntax
...
<StyledSectionColRight>
<Button red type="button" onClick={() => this.validateBeforeDelete(id, image.id)}>Delete</Button>
</StyledSectionColRight>
...
);
}
export default ImagesUpload;
index.js
(Nothing really important, just in case someone thinks the error is due to not exporting deleteImageWarning)
//deleteImageWarning included in the index.js file
export { default as deleteImageWarning } from './deleteImageWarning';
deleteImageWarning.jsx
import React from 'react';
import styled from 'styled-components';
import { Button, ButtonInverted } from '../button';
const StyledNotification = styled.div`
background: var(--white);
padding: var(--spacer-m);
`;
const StyledMessage = styled.p`
font-size: var(--font-s);
line-height: var(--lineHeight-s);
margin: 0 0 var(--spacer-l) 0;
`;
const Content = ({ ????? }) => (
<StyledNotification>
<StyledMessage>
Are you sure you want to delete this image? This process cannot be undone.
</StyledMessage>
<Button type="button" red onClick={?????}>Confirm</Button>
<ButtonInverted type="button">Cancel</ButtonInverted>
</StyledNotification>
);
const deleteImageWarning = (node, ?????) => {
node.addNotification({
content: <Content ?????={?????} />,
type: "info",
title: "",
message: "",
insert: "top",
container: "top-center",
animationIn: ["animated", "fadeIn"],
animationOut: ["animated", "fadeOut"],
dismissable: { click: true },
width: 400
});
}
export default deleteImageWarning;
To make it super obvious, I have added a few question marks in the code to highlight where I don't know what to write.
I think your way of thinking is somewhat (not to be harsh) wrong, please correct me if I am missunderstanding. Where should the Content be rendered? I don't see a place where it would be.
What I would do is make a new Component (instead of a function) and render it only when a flag in the state (e.g. state = { ... , isdeleting = true }) turns true. You might also store the information of the image to be deletet in the state and pass it down to the Component:
//[prev. Code in ImagesUpload.jsx]
{ this.state.isdeleting ? <DeletImgWaring handleDeleteImage = {this.handleDeleteImage} imgInformation={//e.g. this.state.imgToBeDelInfo}
In this way you think more in component-based. And you can reuse the DeletImgWaring-Component somewhere else.
Does this help?
Regards
I'm new to ReactJS and I'm unsure if I'm simply doing this wrong (I'm sure I am to a certain extent!) or I just need to change something.
So Basically I'm pulling in some JSON from a Drupal web service. My content pages are basically just the same page, a header and some paragraph text. So I thought the best way to deal with this was to have one component that fetched and displayed the page which changes by the ID fed into the url.
I'm using React Router Dom (v4), when I reload the page it gets the correct page with the correct ID but if I click on the navigation menu it will only ever show the first page you clicked on, but does update the route in the address bar.
I am using Fetch API and the call is made in componentDidMount, I've googled it a lot and it seemed I need to do something with componentWillReceiveProps as componentDidMount is doing what it's supposed to do and only update when the component is first mounted on the page. But I can't figure out how to use componentWillReceiveProps correctly. I kind of have it working (but not really) if I literally put the same function in both componentWillReceiveProps and componentDidMount but that seems wrong to me... This is my component:
import React from 'react';
var urlForSimplePage = id =>
`page-url/${id}`
class SimplePage extends React.Component {
constructor(props) {
super(props)
this.state = {
requestFailed: false,
open: true,
}
}
fetchSimplePage() {
fetch(urlForSimplePage(this.props.match.params.id))
.then(response => {
if (!response.ok) {
throw Error("Network request failed")
}
return response
})
.then(d => d.json())
.then(d => {
this.setState({
SimplePageData: d
})
}, () => {
this.setState({
requestFailed: true
})
})
}
componentDidMount() {
this.fetchSimplePage();
}
componentWillReceiveProps(nextProps) {
this.fetchSimplePage();
}
render() {
if (this.state.requestFailed) return <p>Failed!</p>
if (!this.state.SimplePageData) return <p>Loading...</p>
return (
<div>
<h1>
{this.state.SimplePageData[0].title[0].value}
</h1>
<div dangerouslySetInnerHTML={ {__html: this.state.SimplePageData[0].body[0].value} } />
</div>
)
}
}
export default SimplePage;