React input checkbox checked - reactjs

I have use React-redux and styled components for my app. I store my initial state theme as a string which is light and dark. then I connect my styled components intial light theme and dark theme in my root app. My dark mood works fine when i used select options but when i used input checkbox it does not work. I never used input checkbox, after reading couple example I used checked and put my initial theme(which is coming from my redux store), then in my handleChange I did, if the event target has dark then dispatch the dark theme. But nothing happening in that handle change. don't know what i am doing wrong.
Here is my toggle component
import React, { useState } from 'react';
import styled from 'styled-components';
import { useDispatch, useSelector } from 'react-redux';
import { appSettings } from '../../state/appSettings';
import { TRootState } from '../../state/index';
export default function Toggle({ }: IProp) {
const dispatch = useDispatch();
const { "appSettings": appSettingState } = useSelector((state: TRootState) => state);
const { theme } = appSettingState || {};
console.log(theme); // inital state which is "light".
return (
<>
{/* This input checkbox does not work */}
<CheckBoxWrapper>
<CheckBox
onChange={(e) => { // This function does not work
e.target.value === `dark` ?
dispatch(appSettings?.actions?.enableDarkTheme()) :
dispatch(appSettings?.actions?.enableLightTheme());
}}
id="toggleSwitch"
type="checkbox"
Checked={theme === `light`}
/>
<CheckBoxLabel htmlFor="toggleSwitch" />
</CheckBoxWrapper>
<br></br>
{/* THIS SELECT OPTIONS WORK FINE. AND I CAN GET DARK AND LIGHT THEME */}
<h2>Theme</h2>
<select
name="theme"
id="theme-select"
value={theme}
onChange={(e) => {
if (e.target.value === `dark`) {
dispatch(appSettings?.actions?.enableDarkTheme());
} else {
dispatch(appSettings?.actions?.enableLightTheme());
}
}}
>
<option value="dark">Dark</option>
<option value="light">Light</option>
</select>
</>
);
}
// This toogle input styled
const CheckBoxWrapper = styled.div`
position: fixed;
top:10px;
right:10px;
`;
const CheckBoxLabel = styled.label`
position: absolute;
top: 0;
left: 0;
width: 42px;
height: 26px;
border-radius: 15px;
background: #bebebe;
cursor: pointer;
&::after {
content: "";
display: block;
border-radius: 50%;
width: 18px;
height: 18px;
margin: 3px;
background: #ffffff;
box-shadow: 1px 3px 3px 1px rgba(0, 0, 0, 0.2);
transition: 0.2s;
}
`;
const CheckBox = styled.input`
opacity: 0;
z-index: 1;
border-radius: 15px;
width: 42px;
height: 26px;
&:checked + ${CheckBoxLabel} {
background: #4fbe79;
&::after {
content: "";
display: block;
border-radius: 50%;
width: 18px;
height: 18px;
margin-left: 21px;
transition: 0.2s;
}
}
`;

Check the change event's checked property. e.target.checked
<CheckBox
onChange={(e: any) => {
e.target.checked
? dispatch(appSettings?.actions?.enableDarkTheme())
: dispatch(appSettings?.actions?.enableLightTheme());
}}
id="toggleSwitch"
type="checkbox"
Checked={theme === `light`}
/>

Related

Implementing animation when removing Toast

I have a working ToastList that enables me to click a button multiple times and generate a toast each time. On entry, I have an animation, but when I remove the toast, I do not get an animation. I am using Typescript and functional components.
My component is as follows:
import React, { useCallback, useEffect, useState } from 'react';
import * as Styled from './Toast.styled';
export interface ToastItem {
id: number;
title: string;
description: string;
backgroundColor: string;
}
export interface ToastProps {
toastList: ToastItem[];
setList: React.Dispatch<React.SetStateAction<ToastItem[]>>;
}
export default function Toast(props: ToastProps) {
const deleteToast = useCallback(
(id: number) => {
const toastListItem = props.toastList.filter((e) => e.id !== id);
props.setList(toastListItem);
},
[props.toastList, props.setList]
);
useEffect(() => {
const interval = setInterval(() => {
if (props.toastList.length) {
deleteToast(props.toastList[0].id);
}
}, 2000);
return () => {
clearInterval(interval);
};
}, [props.toastList, deleteToast]);
return (
<Styled.BottomRight>
{props.toastList.map((toast, i) => (
<Styled.Notification
key={i}
style={{ backgroundColor: toast.backgroundColor }}
>
<button onClick={() => deleteToast(toast.id)}>X</button>
<div>
<Styled.Title>{toast.title}</Styled.Title>
<Styled.Description>{toast.description}</Styled.Description>
</div>
</Styled.Notification>
))}
</Styled.BottomRight>
);
}
And my styling is done using styled-components and is as follows:
import styled, { keyframes } from 'styled-components';
export const Container = styled.div`
font-size: 14px;
position: fixed;
z-index: 10;
& button {
float: right;
background: none;
border: none;
color: #fff;
opacity: 0.8;
cursor: pointer;
}
`;
const toastEnter = keyframes`
from {
transform: translateX(100%);
}
to {
transform: translateX(0%);
}
}
`;
export const BottomRight = styled(Container)`
bottom: 2rem;
right: 1rem;
`;
export const Notification = styled.div`
width: 365px;
color: #fff;
padding: 15px 15px 10px 10px;
margin-bottom: 1rem;
border-radius: 4px;
box-shadow: 0 0 10px #999;
opacity: 0.9;
transition .1s ease;
animation: ${toastEnter} 0.5s;
&:hover {
box-shadow: 0 0 12px #fff;
opacity: 1;
}
`;
export const Title = styled.p`
font-weight: 700;
font-size: 16px;
text-align: left;
margin-bottom: 6px;
`;
export const Description = styled.p`
text-align: left;
`;
When I click a button, I just add an element to the state list, like:
toastProps = {
id: list.length + 1,
title: 'Success',
description: 'Sentence copied to clipboard!',
backgroundColor: '#5cb85c',
};
setList([...list, toastProps]);
My component is rendered like:
<Toast toastList={list} setList={setList}></Toast>
I would like to add animation when a toast exits, but do not know how. I have tried changing the style according to an additional prop I would send to the styled components, but this way all the toasts animate at the same time. My intuition is that I should use useRef(), but I am not sure how. Thanks in advance for any help you can provide.

What is the reason why the pagination style is not getting applied? I am trying to display lists in row but it is not working

Pagination.jsx
import { Box } from "#chakra-ui/react";
import React, { useEffect, useState } from "react";
import ReactPaginate from "react-paginate";
import "./Pagination.css";
export function ReactPagination({ lastPage, filterData }) {
const [pageCount, setPageCount] = useState(0);
useEffect(() => {
setPageCount(lastPage);
}, [lastPage]);
// Invoke when user click to request another page.
const handlePageClick = (event) => {
filterData(event.selected + 1);
};
return (
<Box w="full">
<ReactPaginate
breakLabel="..."
nextLabel="next >"
onPageChange={handlePageClick}
pageRangeDisplayed={4}
pageCount={pageCount}
previousLabel="< previous"
renderOnZeroPageCount={null}
containerClassName="pagination"
previousLinkClassName="pagination__link"
nextLinkClassName="pagination__link"
disabledClassName="pagination__link--disabled"
activeClassName="pagination__link--active"
/>
</Box>
);
}
Pagination.css
//css for react paginate
.pagination {
list-style: none;
display: flex !important;
gap: 4px;
justify-content: space-between;
background: red;
}
.pagination a {
padding: 10px;
border-radius: 5px;
border: 1px solid #6c7ac9;
color: #6c7ac9;
}
.pagination__link {
font-weight: bold;
}
.pagination__link--active a {
color: #fff;
background: #6c7ac9;
}
.pagination__link--disabled a {
color: rgb(198, 197, 202);
border: 1px solid rgb(198, 197, 202);
}
The pagination className in containerClassName is not getting applied but it's strange that other styles like disabledClassName, activeClassName are applied. I am trying to display lists in row but it is not working. Is there any reason or any package updates that is causing this issue?

Why child Styled-components is not applying styles?

I have a Notification component :
import React, { useContext } from "react"
import Wrapper from "./Wrapper"
import context from "./context"
import Close from "./Close"
const Notification = ({ children }) => {
const { type } = useContext(context)
return (<Wrapper type={type}>
<Close /> {// This component does not apply styles !!}
{children}
</Wrapper>)
}
export default Notification
My Wrapper :
import styled from "styled-components"
const Wrapper = styled.div`
position:absolute;
bottom:0;
right:0;
margin-right:1.2em;
margin-bottom:1.2em;
background-color: #fff;
padding:15px;
min-width:350px;
min-height:100px;
border-radius:20px;
box-shadow: 0px 4px 30px 0px ${({ type }) => pickShadowColor(type)}}`
const pickShadowColor = (type) => {
switch (type) {
case "info": return "rgba(51,153,255,0.3)";
case "warning": return "rgba(255,157,0,0.3)";
case "success": return "rgba(0,216,0,0.3)";
case "error": return "rgba(255,0,0,0.3)";
default: return "rgba(190,190,190,0.3)";
}
}
export default Wrapper
The result is :
The problem is with Close component while it has a custom style but it still shows the default style of button.
Close component :
import React from 'react';
import { Button } from './Button';
const Close = () => (
<Button>close</Button>
)
export default Close;
and the styled Button :
import styled from 'styled-components';
export const Button = styled.button({
'background-color': 'red'
})
Expected Behavior
The button should be red.
Actual Behavior
The button has a default style.
Additional Info
Navigator: Chrome.
The button does not accept any styles.
The problem its on box-shadow. You are missing a ; and have an extra {
Change it FROM:
box-shadow: 0px 4px 30px 0px ${({ type }) => pickShadowColor(type)}}`;
TO:
box-shadow: 0px 4px 30px 0px ${({ type }) => pickShadowColor(type)};`;
The complete style:
const Wrapper = styled.div`
position: absolute;
bottom: 0;
right: 0;
margin-right: 1.2em;
margin-bottom: 1.2em;
background-color: #fff;
padding: 15px;
min-width: 350px;
min-height: 100px;
border-radius: 20px;
box-shadow: 0px 4px 30px 0px ${({ type }) => pickShadowColor(type)};
`;

Ref issue using react-hook-form

I'm trying to create a form validation with react-hook-form in my current project. I've already tried different approaches but always I got errors because of the ref attribute. If I change the <FormField> to input, it starts to work.
Any idea how to solve this?
Contact
import React from 'react';
import { useForm } from "react-hook-form";
import FormField from '../../components/FormField';
import Button from '../../components/Button';
const Contact = () => {
const { handleSubmit, register, errors } = useForm();
const onSubmit = values => console.log(values);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<FormField
name="email"
onChange={() => { console.log("changed!") }}
ref={register({
required: "Required",
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: "invalid email address"
}
})}
/>
<p style={{ color: "red" }}>
{errors.email && errors.email.message}
</p>
<Button>Submit</Button>
</form>
);
};
export default Contact;
FormField
import React from "react";
import PropTypes from "prop-types";
import styled, { css } from "styled-components";
const FormFieldWrapper = styled.div`
position: relative;
textarea {
min-height: 150px;
}
input[type="color"] {
padding-left: 67px;
}
`;
const Label = styled.label``;
Label.Text = styled.span`
color: #e5e5e5;
height: 57px;
position: absolute;
top: 0;
left: 16px;
display: flex;
align-items: center;
transform-origin: 0% 0%;
font-size: 18px;
font-style: normal;
font-weight: 300;
transition: 0.1s ease-in-out;
`;
const Input = styled.input`
background: #53585d;
color: #f5f5f5;
display: block;
width: 100%;
height: 57px;
font-size: 18px;
outline: 0;
border: 0;
border-top: 4px solid transparent;
border-bottom: 4px solid #53585d;
padding: 16px 16px;
margin-bottom: 45px;
resize: none;
border-radius: 4px;
transition: border-color 0.3s;
&:focus {
border-bottom-color: var(--primary);
}
&:focus:not([type="color"]) + ${Label.Text} {
transform: scale(0.6) translateY(-10px);
}
${({ value }) => {
const hasValue = value.length > 0;
return (
hasValue &&
css`
&:not([type="color"]) + ${Label.Text} {
transform: scale(0.6) translateY(-10px);
}
`
);
}}
`;
function FormField({ label, type, name, value, onChange, ref }) {
const isTypeTextArea = type === "textarea";
const tag = isTypeTextArea ? "textarea" : "input";
return (
<FormFieldWrapper>
<Label>
<Input
as={tag}
type={type}
value={value}
name={name}
onChange={onChange}
ref={ref}
/>
<Label.Text>{label}:</Label.Text>
</Label>
</FormFieldWrapper>
);
}
FormField.defaultProps = {
type: "text",
value: "",
};
FormField.propTypes = {
label: PropTypes.string,
name: PropTypes.string.isRequired,
type: PropTypes.string,
value: PropTypes.string,
onChange: PropTypes.func,
ref: PropTypes.func
};
export default FormField;
Errors:
Referring to the docs the register should be used as below so we won't get refs issues and also the register will change the value inside the input so we don't need to pass a value prop :
Contact :
import React from "react";
import { useForm } from "react-hook-form";
import FormField from "../../components/FormField";
import Button from "../../components/Button";
const Contact = () => {
const { handleSubmit, register, errors } = useForm();
const onSubmit = (values) => console.log("values", values);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<FormField
name="email"
onChange={() => {
console.log("changed!");
}}
register={register}
/>
<p style={{ color: "red" }}>{errors.email && errors.email.message}</p>
<Button>Submit</Button>
</form>
);
};
export default Contact;
FormField :
import React from "react";
import PropTypes from "prop-types";
import styled, { css } from "styled-components";
const FormFieldWrapper = styled.div`
position: relative;
textarea {
min-height: 150px;
}
input[type="color"] {
padding-left: 67px;
}
`;
const Label = styled.label``;
Label.Text = styled.span`
color: #e5e5e5;
height: 57px;
position: absolute;
top: 0;
left: 16px;
display: flex;
align-items: center;
transform-origin: 0% 0%;
font-size: 18px;
font-style: normal;
font-weight: 300;
transition: 0.1s ease-in-out;
`;
const Input = styled.input`
background: #53585d;
color: #f5f5f5;
display: block;
width: 100%;
height: 57px;
font-size: 18px;
outline: 0;
border: 0;
border-top: 4px solid transparent;
border-bottom: 4px solid #53585d;
padding: 16px 16px;
margin-bottom: 45px;
resize: none;
border-radius: 4px;
transition: border-color 0.3s;
&:focus {
border-bottom-color: var(--primary);
}
&:focus:not([type="color"]) + ${Label.Text} {
transform: scale(0.6) translateY(-10px);
}
${({ value = {} }) => { // here you should find an other approch because there is no value props
const hasValue = value.length > 0;
return (
hasValue &&
css`
&:not([type="color"]) + ${Label.Text} {
transform: scale(0.6) translateY(-10px);
}
`
);
}}
`;
const FormField = ({ label, type, name, onChange, register }) => {
const isTypeTextArea = type === "textarea";
const tag = isTypeTextArea ? "textarea" : "input";
return (
<FormFieldWrapper>
<Label>
<Input
as={tag}
type={type}
// value={value} it's not a controlled input! so the register'ill provide the value
name={name}
onChange={onChange}
ref={register({
required: "Required",
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: "invalid email address",
},
})}
/>
<Label.Text>{label}:</Label.Text>
</Label>
</FormFieldWrapper>
);
};
FormField.defaultProps = {
type: "text",
value: "",
};
FormField.propTypes = {
label: PropTypes.string,
name: PropTypes.string.isRequired,
type: PropTypes.string,
value: PropTypes.string,
onChange: PropTypes.func,
ref: PropTypes.func,
};
export default FormField;

Refs not working

I am trying to do something similar to this. However, I would like for the input field to stay in focus until the user clicks anywhere but within the menu or any of the submenus. I am trying to use refs following the React documentation (trying to duplicate it in fact) but I cannot get it to work.
Error
"Uncaught TypeError: _this.textInput.current.focus is not a function"
Code (input field component)
import React from 'react';
import styled from 'styled-components';
export default class TextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
focusTextInput = () => {
this.textInput.current.focus();
}
render = () => {
return (
<div>
<TextInputField
font={this.props.font}
fontSize={this.props.fontSize}
placeholder={"What?"}
type="text"
value={this.props.title}
titleLength={this.props.titleLength}
onChange={this.props.change}
onFocus={this.props.focus}
onBlur={this.props.blur}
id="titleInput"
textColor={this.props.textColor}
ref={this.textInput} />
<input
type="button"
value="Focus the text input"
onClick={this.focusTextInput}
/>
</div>
);
}
}
const TextInputField = styled.input`
width: 90%;
height: ${props => props.fontSize * 1.25}vw;
line-height: ${props => props.fontSize * 1.25}vw;
text-align: center;
position: absolute;
bottom: -12.5vw;
left: 5%;
margin: 0 auto;
background-color: transparent;
border: transparent;
text-overflow: ellipsis;
color: ${props => props.textColor};
font-size: ${props => props.fontSize}vw;
font-family: ${props => props.font};
&:hover {
background-color: rgba(0, 0, 0, 0.2);
}
&:focus {
outline: none;
background-color: rgba(0, 0, 0, 0.5);
}
`;
you need to attach a innerRef on your styled component, something like this.
const StyledInput = styled.input`
some: styles;
`;
<StyledInput innerRef={comp => this.input = comp} />
// this.input.focus() works 🎉

Resources