React too many re renders infinite loop - javascript

I'm not sure why i'm getting this error, i'm just trying to make a button that inverts colors on hover, if you have a solution or a better way to do this please let me know
import React, {useState} from 'react'
function Login() {
const [bttnColor, setBttnColor] = useState('white')
const [textColor, setTextColor] = useState('black')
function colorChange1(){
setBttnColor('black')
setTextColor('white')
}
function colorChange2(){
setBttnColor('white')
setTextColor('black')
}
return (
<div className="Login" style={{display:'flex', flexDirection:'column', justifyContent:'center', alignItems:'center'}}>
<h1 style={{display:'flex', marginTop:'400px', marginBottom:'40px'}}>Login</h1>
<input style={{height:'30px', width:'140px', marginBottom:'10px'}} placeholder='Username'/>
<input style={{height:'30px', width:'140px'}} type='password'placeholder='Password'/>
<button style={{height:'30px', width:'140px', marginTop:'10px', background:bttnColor, color:textColor}} onMouseEnter={colorChange1()} onMouseLeave={colorChange2()}>Login</button>:
</div>
)
}
export default Login

When declaring a property, the result of what's inside of the {} is sent to the Component.
This will send the result of colorChange1() to the component, not the function itself
onMouseEnter={colorChange1()}
This is unwanted behavior in your use case, but remember that this is a property just like any other, like style or className.
You need to pass it a function reference instead of the result of the function. You can do that in two different ways:
onMouseEnter={colorChange1}
onMouseEnter={(event) => colorChange1(event, otherVariables...)}
The first way is a function reference to the existing function. Use this when you don't need to pass any other information to the function
The second way is to wrap the function call with a lambda. This will allow you to take variables from your current scope and pass them into the method when it's run.
EDIT:
On second thought, doing this at all is making it far more complicated than it needs to be. This can be done in a few lines of CSS.
Let's remove those color change methods and the onMouseEnter and onMouseLeave calls, and give the button a className so we can refer to it in CSS.
<button className="login-button">Login</button>:
Then let's create the following css file, named Login.css in the same folder as Login.js:
.login-button {
height: 30px;
width: 140px;
marginTop: 10px;
background:white;
color:black;
}
.login-button:hover {
background: black;
color: white;
}
Finally, let's import the css file:
import "./Login.css";

Related

How to remove or hide a div when checkbox is checked in React?

I am new to React and I am trying to build a simple todolist app.
Once a task is inserted, a div with an unchecked checkbox appears. Now, I'd like to hide or remove the div when the checkbox is clicked. I think I hooked up everything correctly but I miss, conceptually, what is the best way to do it: should I make a className attribute that changes upon clicking the checkbox? Should I modify the tasks array containing by removing the task that has been checked? Or, is there any other good way I could achieve this?
Thank you so much for your help.
https://scrimba.com/scrim/cof7e4dde89963549cc216a25 Here's the directly editable code
.insert {
margin: 0 auto;
display: block;
padding: 20px;
}
.enter--task {
border-radius: 5px;
}
.tasks {
width: 40%;
min-height: 100%;
color: black;
background-color: white;
margin-top: 20px;
border-radius: 15px;
line-height: 40px;
padding: 10px;
overflow-wrap: break-word;
}
.task--check {
margin: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
import React from "react"
export default function Insert () {
const [task, setTask] = React.useState ("") // settin state for the single task, e.g. the input.
const [taskArray, setTaskArray] = React.useState ([]) // setting state for the array of tasks.
const tasks = taskArray.map((tasks) => {
// {event.target.checked ? "hidden": "tasks"} this on className did not work.
return (
<div className="tasks">
<input
type="checkbox"
className="task--check"
onChange={handleCheck}
/>
{tasks}
</div>
)
}) // This is basically to return a series of paragraphs (soon divs) once enter or add task are clicked.
//How to let the task div disappear upon checking the checkbox?
function handleCheck() {
console.log(event.target.checked ? "checked": "unchecked")
}
function handleChange (event) {
setTask(event.target.value)
} // this is a function to register the value of the input. We use the setTask function to update the value of task to the value present in the input, which in this case it is our target
function handleSubmit (event) {
event.preventDefault()
setTaskArray(task ? [...taskArray,task] : [...taskArray]) // quite hyped! Problem: user can input empty tasks. Solution: ternary operator saying, if task is true (truthy in this case, meaning a string with some content), add it to the taskArray. Otherwise, just return taskArray without appending the falsy task (empty string).
setTask("")
}
// this function handles the event after clicking enter or add task. First, we use event.preventDefault(): this is to avoid that every time that we click enter or add task, the page refreshes and the input is attached to the url (try to remove and see what happens). Then we have to make another array to insert the new task. To do this, we spread the previous array (taskArray) and we add the new element, which is task. After that, we reset the value of task to an empty string - this is to show the placeholder again.
return (
<main className="insert">
<form onSubmit={handleSubmit}>
<input
type="text"
className="enter--task"
placeholder="Enter your task"
onChange={handleChange}
name="task"
value={task} // setting the value of my input equal to my state
/>
<button>Add task</button>
</form>
{tasks}
</main>
)
}
Create a state called checkState
const [checkState, setCheckState] = React.useState(false)
const handleCheck = (event) => {
setCheckState(event.checked);
}
<div className="tasks">
<input
type="checkbox"
checked={checkState}
onChange={handleCheck}
/>
{tasks}
</div>
{checkState && <div>Show div when checkbox checked</div>}
I guess it depends on what you want to achieve, in the classic todo list example, we would remove the task from the state. By hiding it, you would keep the reference in memory. I'm not sure to see a good use case for it.
If you want to remove, you could do it like this. Note that I'm using the index as key which is not recommended as the order of the items could change, instead you should add an id attribute to your tasks, and use it as the key. You will also use it to remove the task.
const tasks = taskArray.map((task, i) => {
return (
<div className="tasks" key={i}>
<input
type="checkbox"
className="task--check"
onChange={() => removeTask(i)}
/>
{task}
</div>
)
})
function removeTask(index) {
setTaskArray(prev => prev.filter((_,i) => i !== index))
}

How to pass custom data-attribute to styled component?

I have this Styled component, where I'm trying to pass DATA-attribute which is coming from props to it. (This is the solution we have on Stack Overflow)
export const InlineEditReadViewErrorContainer = styled.div.attrs(props => ({
'data-cy': props.dataCy
}))`
border: 2px solid #de350b;
border-radius: 3px;
`;
This is how I use this styled component in code
<InlineEditReadViewErrorContainer dataCy='blabla'>
{readView}
</InlineEditReadViewErrorContainer>
But this is doesn't change anything
I think that we must use correctly the attributes that are added to a component in React and more if they are needed only to style a component.
We should use as many native properties as possible but without compromising the private data that would be exposed in the HTML that the client displays in broser, therefore:
If you are going to use data-attributes:
Remember how to name these attributes:
The attribute name should not contain any uppercase letters, and must be at least one character long after the prefix "data-"
Note: I would just use the simplest possible, with booleans to give a set of properties as the first answer described, for example:
component.js
<Error data-active={true}>
{readView}
</Error>
component.styles.js
export const Error = styled.div`
&[data-active="true"] {
border: 2px solid #de350b;
border-radius: 3px;
}
`;
If you want to use custom props without them being displayed in the DOM as the second comment has described, using transient props:
For sample:
component.js
<Error $active={true}>
{readView}
</Error>
component.styles.js
export const Error = styled.div`
${({$active}) => $active ? css`
border: 2px solid #de350b;
border-radius: 3px;
`: null}
`;
The prop should already be "passed" in the sense that it will show up on the component for the purposes of using it in Cypress. If you want to use it internally you could also use transient props such as this
const Comp = styled.div`
color: ${props =>
props.$draggable || 'black'};
`;
render(
<Comp $draggable="red" draggable="true">
Drag me!
</Comp>
);
It was much easier. You can pass the data attribute directly where you use the styled component and everything will be fine.
<InlineEditReadViewErrorContainer data-cy='dateInput'>
{textValue}
</InlineEditReadViewErrorContainer>
Maybe it's related to your bundler, since you should be able to pass a data attribute to a styled-component directly. However, if you're extending an existing component, be aware that you need to pass it through props. Those two situations will attach the data attribute to the final HTML:
function Text(props) {
return (
<p data-foo={props['data-foo']} className={props.className}>
{props.children}
</p>
);
}
const StyledText = styled(Text)`
color: blueviolet;
`;
const StyledHeading = styled.h1`
color: gray;
`;
export default function App() {
return (
<div>
<StyledHeading data-bar="foo">Hello StackBlitz!</StyledHeading>
<StyledText data-foo="bar">
Start editing to see some magic happen :)
</StyledText>
</div>
);
}

Next.js - Cant apply dynamic class names when using CSS Modules

hoping someone here can help me solve this.
Am trying to build a website through NextJs. One of my pages has some paragraphs and buttons which are styled differently based on states and events. I can get the styling to work as intended when using pure React, and also when using a Global Stylesheet with NextJs; but when I use CSS Modules I cant get it to function as intended.
(Note: I can also get it to work by using a simple ternary like
<h1 className={submitted ? styles.showresult : styles.hideresult}>Correct? {correct}</h1>;
but I have some other scenarios where I need to rely on an multiple ifs and create multiple classes, each with their own styling, so I cant make a simple ternary my final solution.
E.g. this is the file pagex.js
import React from 'react';
import ReactDOM from 'react-dom';
const Pagex = () => {
const [submitted, setSubmitted] = React.useState(false); // whether the submit button was pressed
function calculateScore() {
let correct = 0
let incorrect = 0
//......some scoring logic.....
setSubmitted(true)
}
// function to create a display class based on whether the submit button has been pressed
function displayResult(){
if (submitted === true) {
return "showtheresult"
} else {
return "hidetheresult"
}
}
return (
<section className="results">
<h1 className={displayResult()}>Correct? {correct}</h1>
<h1 className={displayResult()}>Incorrect? {incorrect}</h1>
<button className={displayResult()} onClick={handleMovClick}>An instruction</button>
</section>
</div>
);
};
export default Pagex;
the globals.css file contains
h1.hidetheresult, h3.hidetheresult {
visibility: hidden;
}
h1.showtheresult, h3.showtheresult {
visibility: visible;
}
button.hidetheresult {
border-color: pink;
}
button.showtheresult {
border-color: aqua;
}
Changes when using CSS modules
Add a CSS file in the correct folder with the correct name
(../styles/Pagex.module.css) which contains the same styling shown
above
Additional import in pagex.js import styles from '../styles/Pagex.module.css'
Change reference in the function
within pagex.js
function displayResult(){
if (submitted === true) {
return {styles.showtheresult}
} else {
return {styles.hidetheresult}
}
}
When i do this the '.' in {styles.showtheresult} and {styles.hidetheresult} gets highlighted as an error by vscode with this detail: ',' expected. ts(1005).
Saving the js with a dev server running shows a similar message after trying to compile: Syntax error: Unexpected token, expected "," and the browser shows the same message along with "Failed to compile"
Also tried just passing styles.showtheresult / styles.hidetheresult by removing the curly braces from the displayResult() function. That compiles but nothing happens on the compiled webpage, i.e the class doesnt get updated when the button is pressed and so the styling cant be applied.
Also Tried passing as ${styles.showresult} and ${styles.hideresult} (with `)in the return statement. That also compiles but the page itself gives me an "Unhandled Runtime Error ReferenceError: styles is not defined" message and I cant load the page.
Would highly appreciated if someone could help correct my syntax in the function itself or elsewhere in the code.
Because you asked nicely ;) (just kiddin')
So Next.js is an opinionated framework and uses CSS Modules to enforce component scoped styling.
Basically you define your stylesheet with a name.module.css filename and add regular CSS in it.
.hidetheresult {
visibility: hidden;
}
.showtheresult{
visibility: visible;
}
.btn-hidetheresult {
border-color: pink;
}
.btn-showtheresult {
border-color: aqua;
}
Now to use this, import it like any JS module,
import styles from './styles.module.css'
console.log(styles);
// styles => {
// hidetheresult: 'contact_hidetheresult__3LvIF',
// showtheresult: 'contact_showtheresult__N5XLE',
// 'btn-hidetheresult': 'contact_btn-hidetheresult__3CQHv',
// 'btn-showtheresult': 'contact_btn-showtheresult__1rM1E'
// }
as you can see, the styles are converted to objects and now you can use them
like styles.hidetheresult or styles['btn-hidetheresult'].
Notice the absence of element selector in the stylesheet. That's because CSS Modules rewrite class names, but they don't touch tag names. And in Next.js that is
the default behaviour. i.e it does not allow element tag selectors.
File extensions with *.module.(css | scss | sass) are css modules and they can only target elements using classnames or ids and not using tag names. Although this is possible in other frameworks like create-react-app, it is not possible in next-js.
But you can override it in the next.config.js file. (Beyond the scope of this answer)
There is an article which explains how to override it. - disclaimer: I am the author
Now coming to your use-case, you can do contitional styling like so: (assuming the styles are as per the sample given in the answer)
import React from "react";
import styles from "./styles.module.css";
const PageX = () => {
const [submitted, setSubmitted] = React.useState(false);
const getStyle = () => {
if (submitted) return styles.showtheresult;
else return styles.hidetheresult;
};
const getButtonStyle = () => {
if (submitted) return styles["btn-showtheresult"];
else return styles["btn-hidetheresult"];
};
return (
<div>
<section className="results">
<h1 className={getStyle()}>Correct?</h1>
<h1 className={getStyle()}>Incorrect?</h1>
<button className={getButtonStyle()} onClick={handleMovClick}>
An instruction
</button>
</section>
</div>
);
};
As you add more conditions, the methods do tend to get more complex. This is where the classnames
module comes handy.
import styles from "./styles.module.css";
import clsx from "classnames";
const PageX = () => {
const [submitted, setSubmitted] = React.useState(false);
const headerStyle = clsx({
[styles.showtheresult]: submitted,
[styles.hidetheresult]: !submitted,
});
const btnStyle = clsx({
[styles["btn-showtheresult"]]: submitted,
[styles["btn-hidetheresult"]]: !submitted,
});
return (
<div>
<section className="results">
<h1 className={headerStyle}>Correct?</h1>
<h1 className={headerStyle}>Incorrect?</h1>
<button className={btnStyle} onClick={handleMovClick}>
An instruction
</button>
</section>
</div>
);
};
Here's a CodeSandbox for you to play with:

How can i dynamically assign the props as variables to colors from my own color pallet in scss/css with react?

I am calling a component "MyRadioButton" with following props:
<MyRadioButton
label="Radio Group"
theme="custom-red" //this line
error="Field is required "
radioBtns={options}
id="radioBtns"
name="radioBtns"
getValue={this.getValue}
/>
I have created a react component "MyRadioButton" that will accept color name(theme) as props.
export const MyRadioButton = props => {
const {theme} = props;
return (
<div className="my-radio-buttons"> // need to use theme here
<input
onChange={onChange}
type="radio"
/>
</div>
)}
Based on this prop i want to assign the variable in my components scss file, which will take the color code from my custom defined color pallet.
my-radio-button.scss
/* custom color pallet */
$custom-orange: #F060D6;
$custom-red: #BB532E;
$custom-blue: #4C9FEB;
.my-radio-buttons {
.input{
border: 2px solid $custom-red; // i want to assign the color variable based on input prop value to this property
}
}
I have already tried setting variable at css root with javascript and accessing it with variable function var(), it works fine.
But because of some limitations i dont want to use that approach.
also because the color pallet list is huge, i dont want to use separate classes for all of them.
I am looking for some other solution or different approach.
So you can use a combination of custom css variables and your passed theme property. In you css, you would define the basecolor of the border for example:
.my-radio-buttons {
--theme-color: red;
input {
border: 2px solid var(--theme-color);
}
}
This can be updated by your components via componentDidMount or useEffect with the passed theme:
const MyRadioButton = props => {
const { theme } = props;
React.useEffect(() => {
const input = document.querySelector(".my-radio-buttons input");
input.style.setProperty("--theme-color", props.theme);
}, []);
return (
<div className="my-radio-buttons">
<input />
</div>
);
};
Depending on your code style, you can replace the querySelector with a ref.

How to change text in a <p> tag when hovering a button in ReactJs?

I'm trying to change the text of a <p> to David and Larry accordingly when each button (that has an image inside) has hovered. I have experimented with numerous things and found a way to change the CSS of the button with a function. But I was unable to find anything to change text since <p> is in a different class. Any suggestions to address this problem?
For your information, I have added a CSS color changing function I used earlier to the below code sample.
here's my code.
import React from 'react';
import "./Tri.css";
function Tri() {
function davidon(e) {
e.target.style.background = 'red';
}
function davidoff(e) {
e.target.style.background = 'green';
}
function larryon(e) {
e.target.style.background = 'red';
}
function larryoff(e) {
e.target.style.background = 'green';
}
return (
<div>
<div>
<div>
<button onMouseOver={davidon} onMouseLeave={davidoff}>
<img src={require(`./images/david.png`)} className="david"/>
</button>
<button onMouseOver={larryon} onMouseLeave={larryoff}>
<img src={require(`./images/larry.png`)} className="larry"/>
</button>
</div>
<div className="plex">
<p>Larry Or David?</p>
</div>
</div>
</div>
);
}
export default Tri;
Thanks in advance for you replies.
You need to think more in "React", and use component state and props. The offical documentation is a good place to start.
Here I've got two components.
1) Tri: which has it's own state, and builds the HTML using Button components
2) Button: since you need each button to change color depending on the mouse action it's best to separate that functionality out into a new component so that each instance can have its own state.
(I've intentionally left out the images in this example, but you could pass in a src prop to the button and have that handle the images too if you wanted.)
const { useState } = React;
// `Button` accepts a props object
// Here I've destructured out the button name,
// and the handleHover function
function Button({ name, handleHover }) {
// We initialise the state with "green"
const [ color, setColor ] = useState('green');
function handleColor() {
// We set the new color based on the current color
setColor(color => color === 'red' ? 'green' : 'red');
// And then call the `handleHover` function, passing in `name`
handleHover(name);
}
return (
<button
className={color}
onMouseOver={handleColor}
onMouseLeave={handleColor}
>
{name}
</button>
);
}
function Tri() {
// In `Tri` we set its own state for the name
// initialised to an empty string
const [ name, setName ] = useState('');
// A handler that changes the name
// This is the function we pass to each button
function handleHover(name) {
setName(name);
}
// Set up two buttons using our Button component
// assigning a name to each, and passing in our handler
// Whenever the name (state) is changed the name in the
// paragraph also changes
return (
<div>
<div>
<Button name="Larry" handleHover={handleHover} />
<Button name="David" handleHover={handleHover} />
</div>
<p>{name}</p>
</div>
);
}
ReactDOM.render(
<Tri />,
document.getElementById('react')
);
.red { background-color: red; }
.green { background-color: green; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Try using states. And don't change DOM-nodes dynamically in event handlers. Always use React functionality:
React uses a declarative form of programming (The Virtual DOM specifically). You define variables and set them and React updates the DOM if those change.
useState gives you the opportunity to declare an editable (through a setter function) variable. See Docs on State and Props.
import React from 'react';
import "./Tri.css";
function Tri(props) {
// props as immutable arguments (if needed)
// useState keeps an internal state in the component
let initialTxt = 'Larry Or David?';
const [text, setText] = React.useState(initialTxt);
return (
<div>
<div>
<div>
<button
className="david-btn"
onMouseOver={() => setText('David')}
onMouseLeave={() => setText(initialTxt)}>
<img src={require(`./images/david.png`)} className="david"/>
</button>
<button
className="larry-btn"
onMouseOver={() => setText('Larry')}
onMouseLeave={() => setText(initialTxt)}>>
<img src={require(`./images/larry.png`)} className="larry"/>
</button>
</div>
<div className="plex">
<p>{text}</p>
</div>
</div>
</div>
);
}
Also, extend ./Tri.css with the following code. You could use a style-variable but that would make your code more bloated and unreadable if you have access to CSS.
.david-btn,
.larry-btn {
background-color: green;
}
.david-btn:hover,
.larry-btn:hover {
background-color: red;
}
You are looking for Refs. You can read more about them in documentation.
I've created a simple example (based on your code).
Step by step what I did:
import useRef hook which is used to create reference.
import React, { useRef } from "react";
created reference:
const pTagRef = useRef();
passed reference to your p tag
<div ref={pTagRef} className="plex">
<p>Larry Or David?</p>
</div>
created function which can change the content of this reference where pTagRef.current is DOM element.
function setName(name) {
if (pTagRef.current) {
pTagRef.current.innerText = name;
}
}
called the function whenever name changed
setName("larry");
You should definitely use state for this but I hope this one helps you to get started.

Categories

Resources