I have this code in an index.html file which loads with the application:
$(window).on('load', () => {
$('#one').hide();
$('#oneb').hide();
});
And this affects the component:
import React from 'react';
const Test = (props) => <div id="MyDiv">
<div>
<div id="one">
THIS IS ONE
</div>
{
props.list.map((data, i) => {
return <div id="oneb" key={i}>
THIS IS ONEB
</div>
})
}
</div>
</div>
What is happening here is that div with id="one" will hide BUT id="oneb" will still show up.
Why is this happening? How can I fix this?
You are not doing it the React way. To do it the React way, your component should hold and manipulate some kind of state. Check the example below:
import React from 'react';
class MyAwesomeComponent React.Component {
constructor(){
this.state = {
hide: false
}
}
render(){
const {hide} = this.state;
<React.Fragment>
{
hide
? null
: <div>This is my awesome div that I need to show or hide ;)</div>
}
<button>{hide ? 'Show': 'Hide'}</button>
</React.Fragment>
}
}
export default MyAwesomeComponent;
The code above will hide or show your div. Check the React documentation
Related
I am a beginner in React, and I would like to make a web app with a dashboard like Windows tiles.
I generate them easily. But I want when I click in one of them, that hide the Dash menu and Display the sub-app "Prog1". And of course, reverse it. When I click the close button, it closes the sub-app and returns to the Dash menu (implying that it hides the Prog1 to display Dash).
I can hide the Dash menu with a function like this:
fctHide = () => {
this.setState({ isActive: false });
}
But how can I display the other subApp with maybe a function like fctShow?
Are we forced to put the function inside a class or just make a general display/hide function?
Is there an easier and newer way to do this (with hooks for example).
My app is on Codepen here : codepen
I can show you here on demand.
I'm surprised to see lots of questions or tutorials to show and hide elements from a button but not to switch between class/HTML/template etc. (like React Native router I guess).
Thanks!
Unless you are converting an old app to React, i.e. not writing a pure React app from the ground up, don't use ReactDOM.render multiple times. As you want to share the active state between components, it should live in their closest common ancestor.
I'm not sure how your dashboard should work, but here's a demo. Here, APP is such closest ancestor. You don't need react-router if you are not using URL routes or the History API.
import React from "react";
import "./styles.css";
class Dash extends React.Component {
render() {
const { isActive, fctHide, fctShow } = this.props;
const elements = ["1", "2", "3", "4"];
const items = [];
for (const [index, value] of elements.entries()) {
items.push(
<button
key={index}
onClick={() => {
fctShow(index);
}}
>
{value}
</button>
);
}
// if (isActive) {
return (
<div>
<table>
<tbody>
<tr>
<td> {items} </td>
</tr>
</tbody>
</table>
</div>
);
// } else {
// return null;
// }
}
}
class Prog1 extends React.Component {
render() {
const { isActive, selected, fctHide } = this.props;
if (isActive) {
return (
<div className="contProg1">
<button onClick={fctHide}>Close</button>
<h1>Program 1</h1>
<h2>Test1</h2>
<h2>Test2</h2>
<h2>Test3</h2>
Selected: {selected}
<ul>
<li>AAAAA</li>
<li>BBBBB</li>
<li>CCCCC</li>
</ul>
</div>
);
} else {
return null;
}
}
}
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = { isActive: true, selected: null };
}
fctShow = selected => {
this.setState({ isActive: true, selected });
};
fctHide = () => {
this.setState({ isActive: false });
};
render() {
const { isActive, selected } = this.state;
return (
<>
<Dash
isActive={isActive}
fctHide={this.fctHide}
fctShow={this.fctShow}
/>
<Prog1 isActive={isActive} selected={selected} fctHide={this.fctHide} />
</>
);
}
}
OK ! I finally use as proposed react-router. But I used the Hooks version (with <A>...)
So, the little dashboard is splitted in X parts :
The HTML with only the minimum and root.
<div id="root"></div>
The CSS (nothing to say more)
The sub apps in a specific folder "Apps"
import React from "react";
import { A } from "hookrouter";
class Prog1 extends React.Component {
render() {
return (
<div class="contProg1">
<button class="close">
{" "}
Close
</button>
<h1>Program 1</h1>
<h2>Test1</h2>
<h2>Test2</h2>
<h2>Test3</h2>
<ul>
<li>AAAAA</li>
<li>BBBBB</li>
<li>CCCCC</li>
</ul>
</div>
);
}
}
export default Prog1;
The router page to switch between apps and main Dash.
import React from "react";
import Prog1 from "./Apps/Prog1";
import Prog2 from "./Apps/Prog2";
import Prog3 from "./Apps/Prog3";
import Prog4 from "./Apps/Prog4";
import Dash from "./App";
const routes = {
"/": () => <Dash />,
"/Prog1": () => <Prog1 />,
"/Prog2": () => <Prog2 />,
"/Prog3": () => <Prog3 />,
"/Prog4": () => <Prog4 />
};
export default routes;
The main page, the Dashboard (App.js).
import React from "react";
import { A } from "hookrouter";
const elements = ["1", "2", "3", "4"];
function Dash() {
const items = [];
for (const [index, value] of elements.entries()) {
items.push(
<A href={"/Prog" + (index + 1)}>
<button key={index}>{value}</button>
</A>
);
}
return (
<div className="Dash">
<table>
<tr>
<td> {items} </td>
</tr>
</table>
</div>
);
}
export default Dash;
And to finish, the Index page :
import React from "react";
import { render } from "react-dom";
import "./styles.css";
import { useRoutes } from "hookrouter";
import routes from "./router";
import NoPageFound from "./Apps/404";
function App() {
const routeResult = useRoutes(routes);
return <div className="Dash">{routeResult || <NoPageFound />}</div>;
}
render(<App />, document.getElementById("root"));
It works well. I just need to add something like MemoryRouter or something else to hide the URL and prepare for mobile version.
I'm just a little bit scared when I will insert this part in a Django project.
Or, maybe I should separate it ? (You don't need to answer, I will close I think).
Thanks :)
I got a simple vertical nav panel which nav sections consist of image to the left and some text to the right. It looks like the following:
The code of the nav section is as follows:
import React from 'react';
import { connect } from 'react-redux';
import data from '../../data/data.json'
export default class TopBarMenuItems extends React.Component {
constructor(props) {
super(props);
}
render() {
const MENUITEMS = data.menuData.map((item) => { //pls note use of loop here
let srcPath = require(`../../image/topBarMenuIcons/${item.src}`);
return (
<div className="menuItem" dataset={item.id}>
<a href={item.url} className="topbarLink">
<div>
<img src={srcPath} />
</div>
{item.name}
</a>
</div>
)
});
return MENUITEMS;
}
};
The problem is that when I click on the text (some link 1) I get redirected to the desired page, but when I click on the image, I dont. So the ideas is to get redirected to the same page when either image or text is clicked. Any ideas how to fix it would be welcome?
Thank you.
You can use react-router-dom library
import React from 'react';
import { connect } from 'react-redux';
import data from '../../data/data.json'
import { Link } from 'react-router-dom'
export default class TopBarMenuItems extends React.Component {
constructor(props) {
super(props);
}
render() {
const MENUITEMS = data.menuData.map((item) => {
let srcPath = require(`../../image/topBarMenuIcons/${item.src}`);
return (
<div className="menuItem" dataset={item.id}>
<Link to={item.url} className="topbarLink">
<div>
<img src={srcPath} />
</div>
{item.name}
</Link>
</div>
)
});
return MENUITEMS;
}
};
I'm building a small application in ReactJS, it consists of a grid of buttons with letters as values, what I need to do, is to fill an input field with the letters of the buttons clicked, basically like a keyboard.
I've built the grid with the buttons, each button has a letter, but I'm not sure on how I should code the next part; each button should have two stated, either clicked or not, if its clicked, the letter will appear on the input, if clicked again, it should be removed.
These are my components right now:
Square
import React from "react"
class Square extends React.Component {
render() {
return (
<button type="button" className="square">{this.props.letter}</button>
);
}
}
export default Square;
Input Component
import React from 'react';
class Clear extends React.Component {
render() {
return (
<div className="clear-btn">
<button><span>Clear Word</span><span className="cross-icon">X</span></button>
<input className="cust-input" type="text"/>
</div>
);
}
}
export default Clear;
Main App Component
function App() {
return (
<div className="App">
<div className="container">
<div className="letters">
{LettersJSON.board.map( (letter, index) => <Square key={index} letter={letter}/>)}
</div>
<div className="controls">
<Clear />
</div>
</div>
</div>
);
}
export default App;
If anyone can help me on this it would be great, I don't know what would be a good way to get the value of the button and adding it on the input when clicked.
I imagine this would have to be done with events or something like that, quite honestly I'm just starting to learn React and I'm not sure on how I should arrange all the components so they work together.
This is how the app looks as of now:
Consider the following code, also here is the sandbox for you:
https://codesandbox.io/s/6xpzvpno1r
This is our App component. We will populate the buttons here and give each button its letter, passing it through props. We also give each Button component a state-updater function that will update the state of our App component.
import React from 'react'
import ReactDOM from 'react-dom'
import Button from './Button'
import Input from './Input'
class App extends React.Component {
state = {
letters: ['a', 'b', 'c', 'd', 'e'],
value: '',
}
updateValue = letter => {
console.log('ran')
this.setState({
value: this.state.value + letter,
})
}
createButtons = () => {
const letters = this.state.letters
return letters.map(letter => (
<Button letter={letter} updateValue={this.updateValue} />
))
}
render() {
return (
<div>
{this.createButtons()}
<Input value={this.state.value} />
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
Button component: here we keep call that state-updating function on click and keep track if it has been clicked before.
import React from 'react'
class Button extends React.Component {
state = {
clicked: false,
}
handleOnClick = () => {
if (!this.state.clicked) {
this.props.updateValue(this.props.letter)
this.setState({
clicked: true,
})
}
}
render() {
return (
<button onClick={this.handleOnClick} disabled={this.state.clicked}>
{this.props.letter}
</button>
)
}
}
export default Button
Lastly we have our Input component: which just consumes the value from the parent App component.
import React from 'react'
class Input extends React.Component {
render() {
return <input value={this.props.value} />
}
}
export default Input
Let me know if this is helpful to you. I feel like this essentially provides the principles you need to get your code to work.
Let's break what you want into steps:
Clicking a component should send its letter to the parent component.
That array of letters should be stored in the parent component
The input's value should be the value of that array, but as a string.
1) For the Square component to be clickable, it needs an onClick handler. On click, we'll call a function that's passed into Square from the parent component:
import React from "react"
class Square extends React.Component {
render() {
const { handleClick, letter } = this.props;
return (
<button type="button" className="square" onClick={() => handleClick(letter)}>
{this.props.letter}
</button>
);
}
}
export default Square;
2) Main app controller needs a state property to store the letters that get clicked so we can keep track of them. We also need to pass these letters to the input component.
class App extends Component {
constructor(props) {
super(props);
this.state = {
clickedLetters: [],
};
}
saveClickedLetter(letter) {
const { clickedLetters } = this.state;
const cloneOfClickedLetters = clickedLetters;
cloneOfClickedLetters.push(letter);
this.setState({ clickedLetters: cloneOfClickedLetters });
}
render() {
const { clickedLetters } = this.state;
return (
<div className="App">
<div className="container">
<div className="letters">
{LettersJSON.board.map( (letter, index) => <Square key={index} letter={letter} handleClick={this.saveClickedLetter}/>)}
</div>
<div className="controls">
<Clear clickedLetters={clickedLetters.length > 0 && clickedLetters.join()}/>
</div>
</div>
</div>
)
}
}
export default App;
Finally, let's pass in the clickedLetters prop to input's value attribute:
import React from 'react';
class Clear extends React.Component {
render() {
const { clickedLetters } = this.props;
return (
<div className="clear-btn">
<button><span>Clear Word</span><span className="cross-icon">X</span></button>
<input value={clickedLetters} className="cust-input" type="text"/>
</div>
);
}
}
export default Clear;
Good day!
I am new to React and html2canvas. I am making an app which will take "screenshots" of my DOM using html2canvas then store it to an array of screenshots which will then be also rendered on the screen.
I am storing each <canvas> object received from the html2canvas promise to an array then pass it to my ScreenshotsContainer component which passes the array to the Screenshots component. The Screenshots component will then map the array of <canvas> objects to individual Screenshot components.
In App.js, I am calling the html2canvas function then pass the array to ScreenshotsContainer component
import React, { Component } from 'react';
import ScreenshotsContainer from './containers/ScreenshotsContainer/ScreenshotsContainer'
import html2canvas from 'html2canvas';
import './App.css';
class App extends Component {
state = {
canvasArray: []
}
getScreenshotHandler = () => {
console.log("[Canvas Array from state length:]" + this.state.canvasArray.length)
let canvasArray = this.state.canvasArray;
html2canvas(document.body).then((canvas) => {
canvasArray.push(canvas)
});
console.log("[Canvas Object value: ]" + canvasArray);
this.setState({ canvasArray: canvasArray })
}
render() {
return (
<React.Fragment>
<button onClick={this.getScreenshotHandler}>Get Screenshot</button>
<ScreenshotsContainer canvasArray={this.state.canvasArray} />
</React.Fragment>
);
}
}
export default App;
The ScreenshotsContainer component will pass the received array to the Screenshots component:
import React, { Component } from 'react';
import './ScreenshotsContainer.css'
import Screenshots from '../../components/Screenshots/Screenshots';
class ScreenshotsContainer extends Component {
render() {
return (
<div className="ScreenshotsContainer">
<Screenshots canvasArray={this.props.canvasArray} />
</div>
);
}
}
export default ScreenshotsContainer;
The Screenshots component will map the array and pass each canvas object to the Screenshot component:
import React, { Component } from 'react';
import Screenshot from './Screenshot/Screenshot';
class Screenshots extends Component {
render() {
const screenshots = this.props.canvasArray.map(canvas => {
return (
<Screenshot
key={Math.random}
canvasObj={canvas}
/>
)
})
return (
<React.Fragment>
{screenshots}
</React.Fragment>
);
}
}
export default Screenshots;
Here is the Screenshot component
import React from 'react';
import './Screenshot.css';
const screenshot = (props) => (
<div className="Screenshot" >
<canvas ref={props.canvasObj} style={{
width: '10%',
height: '10%'
}} />
</div>
);
export default screenshot;
What I actually get when pressing the button:
Actual screenshot of my result
I was wondering which part went wrong. Any help would be appreciated.
This particular library works in a specific way (looks like it's doing a lot of "magic" under the hood - you should look at the source code here more specifically the renderer folder inside src)
Saving the canvas to the state inside of an array (the correct react way of doing things) will be a problem as it saves it as a complex object with many methods etc... and we can not render objects... This lib was not written with React in mind...
The code sample below is a simple implementation in React...
Here is a live demo: https://codesandbox.io/s/9y24vwn1py
import React, { Component } from 'react';
import html2canvas from 'html2canvas';
class App extends Component {
constructor(props) {
super(props);
this.captureRef = React.createRef();
this.displayRef = React.createRef();
}
getScreenshotHandler = () => {
html2canvas(this.captureRef.current).then(canvas =>
this.displayRef.current.appendChild(canvas),
);
};
render() {
return (
<div>
<div ref={this.captureRef}>
<h2>This enitre div will be captured and added to the screen</h2>
</div>
<button onClick={this.getScreenshotHandler}>Get Screenshot!</button>
<section>
<h5>Your screenshots will be availbale below</h5>
<div ref={this.displayRef} />
</section>
</div>
);
}
}
export default App;
EDIT: based on the comment below here is yet another workaround:
class App extends Component {
constructor(props) {
super(props);
this.state = { canvasArray: [] };
this.captureRef = React.createRef();
}
getScreenshotHandler = () => {
html2canvas(this.captureRef.current).then(canvas =>
this.setState({
canvasArray: [canvas.toDataURL(), ...this.state.canvasArray],
}),
);
};
renderCanvas = () => {
return this.state.canvasArray.map((canvas, i) => {
return <img key={i} src={canvas} alt="screenshot" />;
});
};
render() {
return (
<div className="wrapper">
<div ref={this.captureRef}>
<p>This enitre div will be captured</p>
</div>
<button onClick={this.getScreenshotHandler}>Get Screenshot!</button>
<section>
<h5>Your screenshots will be availbale below:</h5>
{this.renderCanvas()}
</section>
</div>
);
}
}
Link to live demo: https://codesandbox.io/s/1r213057vq
I am trying to update state of my parent component through child component via setState. below is my parent component:
Fulllayout
import React from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import Header from '../components/header/header.jsx';
import Customizer from '../components/customizer/customizer';
import { Navbar, NavbarBrand, Collapse } from 'reactstrap';
export const settings = {
navbarbg: 'skin1',
sidebarbg: 'skin6',
logobg: 'skin6'
}
class Fulllayout extends React.Component {
constructor(props) {
super(props);
this.state = {
settings: settings
};
}
render() {
return (
<div id="main-wrapper">
<header data-navbarbg={this.state.settings.navbarbg}>
<Navbar expand="md" className={}></Navbar>
</header>
<div className="page-wrapper d-block"></div>
<Customizer />
</div>
);
}
}
export default Fulllayout;
in parent i have defined one constant settings which is exported. It is also given in the this.state. and in header, there is an attribute data-navbarbg={this.state.settings.navbarbg}.
I wanted to change its value dynamically. So, i have one customizer which is imported in parent as a child. Below is the child component:
customizer
import React from 'react';
import { settings } from '../../layouts/fulllayout';
import update from 'immutability-helper';
class Customizer extends React.Component {
constructor(props) {
super(props);
this.navbarbgChange = this.navbarbgChange.bind(this);
this.state = {
settings: settings
};
}
navbarbgChange(e) {
var skin = e.currentTarget.dataset.navbarbg;
var abc = update(this.state.settings, {
navbarbg: { $set: skin }
});
this.setState({ settings: abc });
}
render() {
return (
<aside className="customizer" id="customizer">
<a className="service-panel-toggle text-white"></a>
<div className="customizer-body pt-3">
<div className="mt-3 border-bottom px-3">
<ul className="theme-color mb-2">
<li><a data-navbarbg="skin1" onClick={this.navbarbgChange}></a></li>
</ul>
</div>
</div>
</aside>
);
}
}
export default Customizer;
From customizer, by clicking on color, i wanted to setState of parent to the value given in data-navbarbg attribute.
If i put this code in parent jsx file, it is working fine but for some reasons, this files should be kept separated.
So, what is missing in my code? or the whole approach is wrong? Thanks.
Is there a reason for navbarbgChange to be defined in Customizer?
You could consider moving navbarbgChange to Fulllayout instead.
That way you can do
<li><a data-navbarbg="skin1" onClick={this.props.navbarbgChange}></a></li>
This will ensure that the Fulllayout has the updated background in its state. This also ensures that there is good separation of concerns since settings is defined in the parent and not the child component
In react you can always pass methods from parent to child. Let's write method in the parent to change the state of the parent from the child like this.
class Fulllayout extends React.Component {
constructor(props) {
super(props);
this.state = {
settings: settings
};
}
// This will be passed to the child component
changeForCustomizerState() {
this.setState({
settings: abc
});
}
changeForHeaderState() {
this.setState({
settings: abc
});
}
render() {
return (
<div id="main-wrapper">
<header chageNavbarbg={this.changeForHeaderState}>
<Navbar expand="md" className={}></Navbar>
</header>
<div className="page-wrapper d-block"></div>
<Customizer chageNavbarbg={this.changeForCustomizerState} />
</div>
);
}
}
Then onClick on the child just call the parent method from the child which is passed from the parent.
render() {
return (
<aside className="customizer" id="customizer">
<a className="service-panel-toggle text-white"></a>
<div className="customizer-body pt-3">
<div className="mt-3 border-bottom px-3">
<ul className="theme-color mb-2">
<li><a data-navbarbg="skin1" onClick={this.props.chageNavbarbg}></a></li>
</ul>
</div>
</div>
</aside>
);
}
The state update can be done in Parent from Child using callbacks. Check below code for better understanding
Fulllayout:
updateState = (skin) => {
this.setState({
settings.navbarbg: skin
});
}
render(){
return(
<div>
<Customizer updateState={this.updateState}
</div>
);
}
And in your customizer
navbarbgChange(e){
const skin = e.currentTarget.dataset.navbarbg;
this.props.updateState(skin);
}
OR
Fulllayout:
updateState = (e) => {
const skin = e.currentTarget.dataset.navbarbg;
this.setState({
settings.navbarbg: skin
});
}
render(){
return(
<div>
<Customizer updateState={this.updateState}
</div>
);
}
Customizer: directly pass parent function to onClick
<li><a data-navbarbg="skin1" onClick={this.props.updateState}></a></li>
Also stop using var, and start using let and const mostly. ECMASCRIPT itself argues to avoid using var because of its window scope.