React: Unexpected Behaviour - javascript

I want my code to toggle a person handler, Before it was working but since I split into components, It seem to have broken.
Toggle happens on button click (see inside return statement <
button className={btnClass}
onClick={props.toggler}>Button</button>
Here is my entire cockpit.js file (inside src/components/cockpit/cockpit.js).
import React from 'react';
import classes from './cockpit.css';
const Ccockpit = (props) => {
const assignedClasses = [];
let btnClass = ''
if (props.cocPersonState) {
btnClass = classes.red;
console.log(".......")
}
if (props.cocperson <= 2) {
assignedClasses.push(classes.red)
}
if (props.cocperson <= 1) {
assignedClasses.push(classes.bold)
}
return(
<div className={classes.cockpit}>
<h1> Hi I am react App</h1>
<p className={assignedClasses.join(' ')}>hey </p>
<button className={btnClass}
onClick={props.toggler}>Button</button>
</div>
);
}
export default Ccockpit;
and inside App.js
return (
<div className={classes.App}>
<Ccockpit>
cocPersonState = {this.state.showPerson}
cocperson = {this.state.person.length}
toggler = {this.togglerPersonHandler}
</Ccockpit>
{person}
</div>
)
}
}
and this is my togglerpersonHandler code.
togglerPersonHandler = () => {
const doesShow = this.state.showPerson;
this.setState({
showPerson: !doesShow
});
}
I can't see to figure out that why it won't toggle and console.log/change color to red (It isn't changing the state). Can someone please review and figure out the mistake?

Your JSX still isn't right. Please review the JSX syntax with regards to giving it props/children.
You have this:
<Ccockpit>
cocPersonState = {this.state.showPerson}
cocperson = {this.state.person.length}
toggler = {this.togglerPersonHandler}
</Ccockpit>
But those values aren't children, they're properties. So they need to be in the opening tag, like this:
<Ccockpit
cocPersonState = {this.state.showPerson}
cocperson = {this.state.person.length}
toggler = {this.togglerPersonHandler}/>
Revisit some React tutorials to see how JSX should be structured and how it works.

Related

React tab-like system using CSS classes

I have 5 div's and 5 buttons. On each button clicked one div become visible. the other four gets hidden. I just want to ask is there any other better way to do it. Give suggestion as much as possible. Thank you!
let id1 = React.createRef()
let id2 = React.createRef()
let id3 = React.createRef()
let id4 = React.createRef()
let id5 = React.createRef()
function iid1() {
id1.current.classList.remove('hidden')
id1.current.classList.add('contents')
id2.current.classList.add('hidden')
id3.current.classList.add('hidden')
id4.current.classList.add('hidden')
id5.current.classList.add('hidden')
}
function iid2() {
id1.current.classList.add('hidden')
id2.current.classList.remove('hidden')
id2.current.classList.add('contents')
id3.current.classList.add('hidden')
id4.current.classList.add('hidden')
id5.current.classList.add('hidden')
}
function iid3() {
id1.current.classList.add('hidden')
id2.current.classList.add('hidden')
id3.current.classList.remove('hidden')
id3.current.classList.add('contents')
id4.current.classList.add('hidden')
id5.current.classList.add('hidden')
}
function iid4() {
id1.current.classList.add('hidden')
id2.current.classList.add('hidden')
id3.current.classList.add('hidden')
id4.current.classList.remove('hidden')
id4.current.classList.add('contents')
id5.current.classList.add('hidden')
}
function iid5() {
id1.current.classList.add('hidden')
id2.current.classList.add('hidden')
id3.current.classList.add('hidden')
id4.current.classList.add('hidden')
id5.current.classList.remove('hidden')
id5.current.classList.add('contents')
}
I just want the above code to be more efficient & readable. I'm looking for best practices for javascript. You can also tell me you would you solve this problem. I'm not looking for answer's. I'm here to seek best practices,
Thank you.
Use state to identify which div is the selected one. Buttons will change the state and your app will re-render adjusting the classNames for the divs.
const App = () => {
const [selected,setSelected] = React.useState(0);
const DIV_IDS = [0,1,2,3,4,5];
const selectItems = DIV_IDS.map((item) => {
return(
<button onClick={() => setSelected(item)}>{item}</button>
);
});
const divItems = DIV_IDS.map((item) => {
return (
<div key={item} className={selected === item ? 'visible' : 'hidden'}>
I am div {item}
</div>
);
});
return(
<div>
<div>{selectItems}</div>
<div>{divItems}</div>
</div>
);
};
ReactDOM.render(<App/>, document.getElementById('root'));
.hidden {
visibility: hidden;
}
.visible {
visibility: visible;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
May be best to just have the class in your JSX element classes. Something like:
<element className={(condition_for_shown) ? 'contents' : 'hidden'}>
...
</element>
and then for each button would be:
<button type="button" onClick={() => setStateConditonToSomething}>
...
</button>
Note that you'll need to store the condition in react state with useState or however you wanna store it.
The way i'd do it is -
const DivHidingComponent = ({ elementCount = 5 }) => { // element count defaults to 5
const [visibilityIndex, setVisibilityIndex] = useState(0);
const onClickCallback = useCallback((index) => () => {
setVisibilityIndex(index);
})
const buttonGroup = useMemo(() => {
const buttonGroup = [];
for (let i = 0; i < elementCount; i++) {
buttonGroup.push(
<button key={`${i}-button`} onClick={onClickCallback(i)} />
)
}
return buttonGroup;
}, [elementCount])
// only re-runs on a button click
const divGroup = useMemo(() => {
const divGroup = [];
for (let i = 0; i < elementCount; i++) {
divGroup.push(
<div key={`${i}-div`} style={{ visibility: visibilityIndex === i ? 'visible' : 'hidden' }} />
);
}
return divGroup;
}, [visibilityIndex]);
return (
<div>
<div>
{buttonGroup}
</div>
<div>
{divGroup}
</div>
</div>
);
}
I set the style directly in the div group loop, but you could assign a class name or go about setting the style however you want.
Div's visibility is set by the visibility index that is driven by the buttons being clicked on.
I passed the elementCount variable in the props so you could scale this to however many elements you want. 5 or a 1000. I assigned elementCount a value of 5 that will act as a default for when no value is passed when the component is initialized.
Also, you could drop the useMemo and useCallback hooks and it would still execute fine. But it would help improve performance if you say, set the element count to 10,000. With those hooks in place it'd only re-build the div group on re-render. That'd be the difference between running the loops 20k times (10k for buttons, 10k for divs).
I added the last paragraph incase you were not aware of React Hooks!
I hope this helps!

Add onclick or eventListener in insertAdjacentHTML in react-js

I am building a simple react app for learning purpose, I just started learning react-js, I was trying to add paragraph dynamically on user action and it worked perfectly But I want to add an onClick event in insertAdjacentHTML (basically innerHTML).
But onclick event is not working in innerHTML
app.js
const addParagraph = () => {
var paragraphSpace = document.getElementById('container')
paragraphSpace.insertAdjacentHTML('beforeend', `<p>I am dynamically created paragraph for showing purpose<p> <span id="delete-para" onClick={deleteParagraph(this)}>Delete</span>`
}
const deleteParagraph = (e) => {
document.querySelector(e).parent('div').remove();
}
class App extends React.Component {
render() {
return (
<div>
<div onClick={addParagraph}>
Click here to Add Paragraph
</div>
<div id="container"></div>
</div>
)
}
}
What I am trying to do ?
User will be able to add multiple paragraphs and I am trying to add a delete button on every paragraph so user can delete particular paragraph
I have also tried with eventListener like :-
const deleteParagraph = () => {
document.querySelector('#delete').addEventListener("click", "#delete",
function(e) {
e.preventDefault();
document.querySelector(this).parent('div').remove();
})
}
But It said
deleteParagraph is not defined
I also tried to wrap deleteParagraph in componentDidMount() But it removes everything from the window.
Any help would be much Appreciated. Thank You.
Do not manipulate the DOM directly, let React handle DOM changes instead. Here's one way to implement it properly.
class App extends React.Component {
state = { paragraphs: [] };
addParagraph = () => {
// do not mutate the state directly, make a clone
const newParagraphs = this.state.paragraphs.slice(0);
// and mutate the clone, add a new paragraph
newParagraphs.push('I am dynamically created paragraph for showing purpose');
// then update the paragraphs in the state
this.setState({ paragraphs: newParagraphs });
};
deleteParagraph = (index) => () => {
// do not mutate the state directly, make a clone
const newParagraphs = this.state.paragraphs.slice(0);
// and mutate the clone, delete the current paragraph
newParagraphs.splice(index, 1);
// then update the paragraphs in the state
this.setState({ paragraphs: newParagraphs });
};
render() {
return (
<div>
<div onClick={this.addParagraph}>Click here to Add Paragraph</div>
<div id="container">
{this.state.paragraphs.map((paragraph, index) => (
<>
<p>{paragraph}</p>
<span onClick={this.deleteParagraph(index)}>Delete</span>
</>
))}
</div>
</div>
);
}
}
insertAdjecentHTML should not be used in javascripts frameworks because they work on entirely different paradigm. React components are rerendered every time you change a component state.
So you want to manipulate look of your component by changing its state
Solution:
In constructor initialize your component's state which you will change later on button click. Initial state is array of empty paragraphs.
constructor() {
super()
this.state = {
paragraphs:[]
}
}
And alter that state on button click - like this:
<div onClick={addParagraph}>
Add Paragraph function
const addParagraph = () =>{
this.state = this.state.push('New paragraph')
}
Rendering paragraphs
<div id="container">
this.state.paragraphs.map(paragraph =>{
<p>{paragraph}</p>
})
</div>
Additional tip for ReactJS in 2022 - use Functional components instead of Class components

createRef().current is coming as null in React

so basically my usecase is that when i click the download button present in each row of the table, my data returned should be downloaded via CSV.
I'm creating the ref like this
class ViewStorageGroups extends Component {
csvLink = React.createRef();
This is my custom component which renders a table
<TableWithHeaders
tableHeaders = {tableData.tableHeaders}
rowData = {tableData.rowData}
formatter = {tableData.formatter}
handleClick = {this.handleClick}
clickable = {tableData.clickable}
page = {rowsPerPage}
rowsPerPage = {rowsPerPage}
handlePageChange = {this.handlePageChange}
handleChangeRowsPerPage = {this.handleChangeRowsPerPage}
/>
this is my handleClick function which gets called when i press the download button
handleClick = (rowData, clickComponent,e) => {
const storageGroupDto = StorageGroupHelpers.convertStorageGroupDataToDto(rowData)
this.setState({groupDataDownloadResp: storageGroupDto}, () => this.csvLink.current.link.click())
}
and i'm using React's CSVLink component to download the data as csv.
renderDownloadedData = () => {
const fileName = createFileNameForCSVDownload('STORAGE_GROUP_DOWNLOAD', getWarehouseIdFromRedux(this.props))
return (
<div>
<CSVLink
ref={this.csvLink}
data={this.state.groupDataDownloadResp}
filename={fileName}
target='_blank'
/>
</div>
)
}
Now the issue is when handleClick is triggered, my this.csvLink.current is coming as null. the same implementation is present at other places in my codebase and working fine correct. i'm not able to figure out where i'm going wrong
I have also tried initialising csvLink = React.createRef(); at componenentDidMount and the constructor but still the same error is occuring.
finally got the issue but not sure the Reason.
if i use
<CSVLink
ref={this.csvLink}
data={this.state.groupDataDownloadResp}
filename={fileName}
target='_blank'
/>
inside a arrow function like this
renderDownloadedData = () => {
const fileName = createFileNameForCSVDownload('STORAGE_GROUP_DOWNLOAD', getWarehouseIdFromRedux(this.props))
return (
<div>
<CSVLink
ref={this.csvLink}
data={this.state.groupDataDownloadResp}
filename={fileName}
target='_blank'
/>
</div>
)
}
and then call this inside render like this
render () {
return (
{this.renderDownloadedData()}
)
}
It returns reactRef()'s current value as null. however if I dont use arrow function and use the function like this renderDownloadedData (), it works perfectly fine. if someone can explain me the reason i would be very grateful

Reset Render on page reload UseEffect and Css

Im starting with react and im trying to make a horizontal scrolling page. It seems to work just fine except for one thing, of which i'm pretty certain i'm missing some React logic for this.
I use a targetContainer div with in it, several pages (fullscreen) and a Navbuttons class to move it around.
In my code below i use a 'NavButtons' functional component that sets the targetContainers 'left' value.
But when I reload the page with F5, my page stays on set style (e.g. left:-300%) but pageCounter goes back to 0, breaking the nav buttons...
I'm pretty certain its because i'm using the css-style but what's the right/best way to solve this?
import React, { useEffect, useState } from 'react';
const NavButtons = (props) => {
const maxCount = props.maxCount;
const [pageCounter, setPageCounter] = useState(0);
const scrollPrev = function () {
if (pageCounter > 0) {
setPageCounter(pageCounter - 1);
}
}
const scrollNext = function () {
if (pageCounter < (maxCount - 1)) {
setPageCounter(pageCounter + 1);
}
}
useEffect(() => {
props.targetContainer.current.style.left = -((pageCounter) * 100) + 'vw';
}, [pageCounter, props.targetContainer]);
useEffect(() => {
setToZero();
}, []);
const setToZero = function () {
setPageCounter(0);
props.targetContainer.current.style.left = 0;
}
return (
<div className="NavButtons">
<button onClick={scrollPrev}>Prev</button>
<button onClick={scrollNext}>Next</button>
</div>
)
}
export default NavButtons;
Here is a stackblitz,
https://react-zyvu7o.stackblitz.io/
Edit on:
https://stackblitz.com/edit/react-zyvu7o?file=src/components/Navbuttons.js
It 'unfortunately' works normal on stackblits, but not on my localhost... :(
I'm fairly confident this only occurs due to browser caching & hot reloading, which is why it's working in your example and not locally.

document.execCommand ('copy') don't work in React

I have the function below that is called on click of a button . Everything works well, but the document.execCommand ('copy') simply does not work.
If I create another button and call only the contents of if in a separate function, it works well.
I have already tried calling a second function inside the first one, but it also does not work. the copy is only working if it is alone in the function.
Does anyone know what's going on?
copyNshort = () => {
const bitly = new BitlyClient('...') // Generic Access Token bit.ly
let txt = document.getElementById('link-result')
bitly.shorten(txt.value)
.then((res) => {
this.setState({ shortedLink: res.url })
if (this.state.shortedLink !== undefined) {
document.getElementById('link-result-shorted').select() // get textarea value and select
document.execCommand('copy') // copy selected
console.log('The link has been shortened and copied to clipboard!')
ReactDOM.render(<i className="fas fa-clipboard-check"></i>, document.getElementById('copied'))
}
console.log('Shortened link 👉🏼', res.url) // Shorted url
})
}
The problem is that the copy-to-clipboard functionality will only work as a direct result of a user's click event listener... This event cannot be virtualised and the execCommand will not work anywhere else than the immediate callback assigned to the event listener...
Because react virtualises and abstracts 'events' then that's very possibly where the problem lies and as suggested you should be using React's react-copy-to-clipboard.
You can use lib react-copy-to-clipboard to copy text.
import {CopyToClipboard} from 'react-copy-to-clipboard';`
function(props) {
return (
<CopyToClipboard text={'Text will be copied'}>
<button>Copy button</button>
</CopyToClipboard>
);
}
if you click button Copy button, it will copy the text Text will be copied
The lib react-copy-to-clipboard based on copy-to-clipboard does work for me, but if you want to copy the source into your own file, Some places need attention.
The code below works fine.
import React, { Component } from 'react'
class App extends Component {
render() {
return (
<div className="App">
<h1
onClick={e => {
const range = document.createRange()
const selection = document.getSelection()
const mark = document.createElement('span')
mark.textContent = 'text to copy'
// reset user styles for span element
mark.style.all = 'unset'
// prevents scrolling to the end of the page
mark.style.position = 'fixed'
mark.style.top = 0
mark.style.clip = 'rect(0, 0, 0, 0)'
// used to preserve spaces and line breaks
mark.style.whiteSpace = 'pre'
// do not inherit user-select (it may be `none`)
mark.style.webkitUserSelect = 'text'
mark.style.MozUserSelect = 'text'
mark.style.msUserSelect = 'text'
mark.style.userSelect = 'text'
mark.addEventListener('copy', function(e) {
e.stopPropagation()
})
document.body.appendChild(mark)
// The following line is very important
if (selection.rangeCount > 0) {
selection.removeAllRanges()
}
range.selectNodeContents(mark)
selection.addRange(range)
document.execCommand('copy')
document.body.removeChild(mark)
}}
>
Click to Copy Text
</h1>
</div>
)
}
}
export default App
import React, { Component } from 'react'
class App extends Component {
render() {
return (
<div className="App">
<h1
onClick={e => {
const mark = document.createElement('textarea')
mark.setAttribute('readonly', 'readonly')
mark.value = 'copy me'
mark.style.position = 'fixed'
mark.style.top = 0
mark.style.clip = 'rect(0, 0, 0, 0)'
document.body.appendChild(mark)
mark.select()
document.execCommand('copy')
document.body.removeChild(mark)
}}
>
Click to Copy Text
</h1>
</div>
)
}
}
export default App

Categories

Resources