I am trying to fade a text using onChange event of a range slider in react.js.
As you can see in attached image, for an example, when I click 2(s), the text line of "This is Video Title" should fade out after 2(s) passed.
The Problem is, I can't understand how to link correspondent values of range slider labels as an onChange function to fade the text. (Before, I executed the same objective using button click events, but now I need to do that using a range slider). I hope you can understand my question.
Following are my working files in this regard. And, I kindly request your assistance in this regard. Thank you very much.
(In App.js, I have used Switch case to capture the set time values of 0s, 2s, 5s, 7s and 10s by using their relevant labels. But, I'm not sure whether it is a correct approach. Please consider, App.js is half-completed and that's where I have got stuck for hours.
In DiscreteSlider.js, I have assigned different values for marks, and please ignore the correspondence of set values and their labels.)
App.js
import React, { useEffect, useState } from "react";
import "./App.css";
import DiscreteSlider from "./DiscreteSlider.js";
function App() {
const [time, setTime] = useState(0);
const [showText, setShowText] = useState(true);
const textFadeOutTimer = (e) => {
switch (e.target.value) {
case "0":
setTime(0);
break;
case "20":
setTime(2000);
break;
case "40":
setTime(5000);
break;
case "70":
setTime(7000);
break;
case "100":
setTime(10000);
break;
}
};
useEffect(() => {
if (time !== 0) {
const timeout = setTimeout(() => {
setShowText(false);
}, time);
return () => clearInterval(timeout);
}
}, [time]);
const textFader = () => {
setShowText(false);
};
return (
<>
<div className={showText ? "showTextOn" : "showTextOff"}>
This is Video Title
</div>
</>
);
}
export default App;
DiscreteSlider.js
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Slider from "#material-ui/core/Slider";
const useStyles = makeStyles({
root: {
width: 500,
},
});
const marks = [
{
value: 0,
label: "0(s)",
},
{
value: 20,
label: "2(s)",
},
{
value: 40,
label: "5(s)",
},
{
value: 70,
label: "7(s)",
},
{
value: 100,
label: "10(s)",
},
];
function DiscreteSlider(props) {
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
props.onChange(newValue);
};
const classes = useStyles();
return (
<div className={classes.root}>
<Slider
defaultValue={0}
step={null}
marks={marks}
onChange={handleChange}
/>
</div>
);
}
export default DiscreteSlider;
To fade the text, you can use the opacity style from CSS for HTML element.
Provide the DiscreteSlider component with the event and use that event in the App component.
Create a new state.
const [Opa, setOpa] = useState(0);
created a new method in App component onChangeApp
function onChangeApp(value) {
setOpa(value / 100);
}
return method of App component
return (
<>
<div style={{ opacity: Opa }}>This is Video Title</div>
<DiscreteSlider onChange={onChangeApp} />
</>
);
You can checkout the solution in the stackblitz project i created for this.
https://stackblitz.com/edit/react-fade-element?file=src/App.js
Related
Ive been trying to make a dynamic input field(more input options appear on user input) with react-select, the input gets displayed when I'm not modifying value prop using state variables, but when I modify value prop using state hooks it is not displaying anything.
Here is the code snippet for without hooks which displays output just fine
import React,{useState} from "react"
import CreatableSelect from 'react-select/creatable';
export default function DynamicInput(){
const [val,setVal] = useState([])
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' }
]
const handleAdd=()=>{
const tempVal = [...val,[]]
setVal(tempVal)
}
const handleDel=(indx)=>{
const deleteVal = [...val]
deleteVal.splice(indx,1);
setVal(deleteVal);
}
return (
<div>
<button onClick={()=>handleAdd()}>Add</button>
{val.map((data,indx)=>{
return(
<div key = {indx}>
<CreatableSelect isClearable options={options} placeholder="Placeholder" } />
<button onClick = {()=>handleDel(indx)}>X</button>
</div>
)
})
}
</div>
);
}
Now I tried to use hooks to handle the input hooks.
import React, { useState } from "react";
import CreatableSelect from "react-select/creatable";
export function DynamicInputwHooks() {
const [val, setVal] = useState([]);
const [selectedOp, setSelectedOp] = useState([]);
const options = [
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" }
];
const handleAdd = () => {
const tempVal = [...val, []];
const tempSel = [...selectedOp, []];
setSelectedOp(tempSel);
setVal(tempVal);
};
const handleSelection = (v, indx) => {
const tempSel = [...selectedOp];
tempSel[indx] = v.value;
setSelectedOp(tempSel);
};
const handleDel = (indx) => {
const deleteVal = [...val];
const deletesel = [...selectedOp];
deleteVal.splice(indx, 1);
deletesel.splice(indx, 1);
setVal(deleteVal);
setSelectedOp(deletesel);
};
return (
<div>
<button onClick={() => handleAdd()}>Add</button>
{val.map((data, indx) => {
return (
<div key={indx}>
<CreatableSelect
isClearable
options={options}
placeholder={"Placeholder"}
value={selectedOp[indx]}
onChange={(e) => handleSelection(e, indx)}
/>
<button onClick={() => handleDel(indx)}>X</button>
</div>
);
})}
</div>
);
}
I also added an input box with the same value and it displays value accordingly.
(I am unable to embed code using sandbox but this is the link : https://codesandbox.io/s/frosty-darwin-s3ztee?file=/src/App.js)
Upon using inspect element I found that when not using handling value there is an additional div as compared to when modifying value.
When not handling value prop
The single value div
when handling value prop
No single value div gets created
I cannot use useRef as there can be multiple inputs.
any help how to solve this would be appreciated thanks.
Ok so after some more searching I found this sandbox example that helps solve the problem
https://codesandbox.io/s/react-select-set-value-example-d21pt?file=/src/App.js
while this works for react-select but using it for react-select creatable requires appending to the options based on isNew prop of input when creating a new option.
I am trying to create a simple button multi-select in React but I'm currently getting unexpected behaviour. I'd like users to be able to toggle multiple buttons and have them colourise accordingly, however the buttons seem to act a bit randomly.
I have the following class
export default function App() {
const [value, setValue] = useState([]);
const [listButtons, setListButtons] = useState([]);
const BUTTONS = [
{ id: 123, title: 'button1' },
{ id: 456, title: 'button2' },
{ id: 789, title: 'button3' },
];
const handleButton = (button) => {
if (value.includes(button)) {
setValue(value.filter((el) => el !== button));
} else {
let tmp = value;
tmp.push(button);
setValue(tmp);
}
console.log(value);
};
const buttonList = () => {
setListButtons(
BUTTONS.map((bt) => (
<button
key={bt.id}
onClick={() => handleButton(bt.id)}
className={value.includes(bt.id) ? 'buttonPressed' : 'button'}
>
{bt.title}
</button>
))
);
};
useEffect(() => {
buttonList();
}, [value]);
return (
<div>
<h1>Hello StackBlitz!</h1>
<div>{listButtons}</div>
</div>
);
}
If you select all 3 buttons then select 1 more button the css will change.
I am trying to use these as buttons as toggle switches.
I have an example running #
Stackblitz
Any help is much appreciated.
Thanks
I think that what you want to achieve is way simpler:
You just need to store the current ID of the selected button.
Never store an array of JSX elements inside a state. It is not how react works. Decouple, only store the info. React component is always a consequence of a pattern / data, never a source.
You only need to store the necessary information, aka the button id.
Information that doesn't belong to the state of the component should be moved outside. In this case, BUTTONS shouldn't be inside your <App>.
Working code:
import React, { useState } from 'react';
import './style.css';
const BUTTONS = [
{ id: 123, title: 'button1', selected: false },
{ id: 456, title: 'button2', selected: false },
{ id: 789, title: 'button3', selected: false },
];
export default function App() {
const [buttons, setButtons] = useState(BUTTONS);
const handleButton = (buttonId) => {
const newButtons = buttons.map((btn) => {
if (btn.id !== buttonId) return btn;
btn.selected = !btn.selected;
return btn;
});
setButtons(newButtons);
};
return (
<div>
<h1>Hello StackBlitz!</h1>
<div>
{buttons.map((bt) => (
<button
key={bt.id}
onClick={() => handleButton(bt.id)}
className={bt.selected ? 'buttonPressed' : 'button'}
>
{bt.title}
</button>
))}
</div>
</div>
);
}
I hope it helps.
Edit: the BUTTONS array was modified to add a selected property. Now several buttons can be selected at the same time.
I am using react-select. But I don't know how to get the value of the currently highlighted option from the list options.
E.g. if a user pressed the key down or up button, I want to know which option is selected.
I haven't found any usable props in the documentation.
Not looking solutions like below.
Get value of highlighted option in React-Select
Sadly the library doesn't provide such a feature. However it applies the [class-prefix]__option--is-focused to the option that is focused. You can then easily get the value you want by checking classes change in pure Javascript.
This answer implement the class ClassWatcher that enable you to check class addition or removal on a specific node like:
new ClassWatcher(targetNode, 'your-class', onClassAdd, onClassRemoval)
So you could add this watcher to each options of the select by using querySelectorAll on the ref of the select. First step is to initialised the component with a few options and some states like isMenuOpen, focusedValue and the selectedOption:
const OPTIONS = [
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" }
];
export default function App() {
const [isMenuOpen, setIsMenuOpen] = React.useState(false);
const [focusedValue, setFocusedValue] = React.useState("");
const [selectedOption, setSelectedOption] = React.useState(null);
const ref = React.useRef(null);
return (
<div className="App">
<p>Focused value: {focusedValue}</p>
<Select
ref={ref}
classNamePrefix="my-select"
value={selectedOption}
onChange={setSelectedOption}
options={OPTIONS}
isMenuOpen={isMenuOpen}
onMenuOpen={() => setIsMenuOpen(true)}
onMenuClose={() => {
setFocusedValue("");
setIsMenuOpen(false);
}}
/>
</div>
);
}
Now we can use ClassWatcher to update the focusedValue state when the class my-select__option--is-focused change. This as to be done when the ref is not null and when the menu is open so we can use a useEffect hook for that:
React.useEffect(() => {
if (ref && isMenuOpen) {
const menu = ref.current.select.menuListRef;
const options = menu.querySelectorAll(".my-select__option");
// add class watcher to each options
options.forEach((option, index) => {
new ClassWatcher(
option,
"my-select__option--is-focused",
() => setFocusedValue(OPTIONS[index].value),
() => {}
);
});
}
}, [ref, isMenuOpen]);
You can check here the complete example:
The Option component has an isFocused prop you that could be used. I'm looking at injecting a ref into the custom option prop and whenever that option is focus, update the ref to the value of that option.
import React from "react";
import Select, { components, OptionProps } from "react-select";
import { ColourOption, colourOptions } from "./docs/data";
export default () => {
const focusdRef = React.useRef(colourOptions[4]);
const Option = (props: OptionProps<ColourOption>) => {
const { isFocused, data } = props;
if (isFocused) focusdRef.current = data;
return <components.Option {...props} />;
};
return (
<Select
closeMenuOnSelect={false}
components={{ Option }}
styles={{
option: (base) => ({
...base,
border: `1px dotted ${colourOptions[2].color}`,
height: "100%"
})
}}
defaultValue={colourOptions[4]}
options={colourOptions}
onKeyDown={(e) => {
if (e.key === "ArrowRight") {
console.log(focusdRef.current);
}
}}
/>
);
};
So here whenever you press the right arrow, you have access to the current focused value.
code sandbox:
import React, { useEffect, useRef, useState } from "react";
import WaveSurfer from "wavesurfer.js";
const formWaveSurferOptions = ref => ({
container: ref,
barWidth:1,
waveColor: "#eee",
progressColor: "OrangeRed",
cursorColor: "OrangeRed",
barRadius: 10,
responsive: true,
height: 200,
barGap:0,
pixelRatio: 5,
barMinHeight:100,
normalize: true,
partialRender: true
});
export default function Waveform({ url }) {
const waveformRef = useRef(null);
const wavesurfer = useRef(null);
const [playing, setPlay] = useState(false);
const [volume, setVolume] = useState(0.5);
useEffect(() => {
setPlay(false);
const options = formWaveSurferOptions(waveformRef.current);
wavesurfer.current = WaveSurfer.create(options);
wavesurfer.current.load(url);
wavesurfer.current.on("ready", function() {
if (wavesurfer.current) {
wavesurfer.current.setVolume(volume);
setVolume(volume);
}
});
return () => wavesurfer.current.destroy();
}, [url]);
const handlePlayPause = () => {
...
};
const onVolumeChange = e => {
...
};
return (
<div>
<div id="waveform" ref={waveformRef} />
<div className="controls">
<button onClick={handlePlayPause}>{!playing ? "Play" : "Pause"}</button>
<input
...
/>
<label htmlFor="volume">Volume</label>
</div>
</div>
);
}
The problem in this is that, i want to set the height of the minpeak of the wave and also give the height an average that at this level it does not go up. i tried the minHeightBar, but unfortunatelydoes't work at all, I have connected the two images for better understanding, so if any one knows this so please help, thankyou :)
I have a material ui stepper in which there are multiple forms using react-hook-form. When I change between steps, I would expect a new useForm hook to create new register functions that would register new form fields, but when I switch between steps, the second form has the data from the first. I've seen at least one question where someone was trying to create two forms on the same page within the same component but in this case I am trying to create two unique forms within different steps using different instances of a component. It seems like react-hook-form is somehow not updating the useForm hook or is recycling the form fields added to the first register call.
Why isn't react-hook-form using a new register function to register form fields to a new useForm hook? Or at least, why isn't a new useForm hook being created between steps?
DynamicForm component. There are two of these components (one for each step in the stepper).
import { Button, Grid } from "#material-ui/core";
import React from "react";
import { useForm } from "react-hook-form";
import { buttonStyles } from "../../../styles/buttonStyles";
import AppCard from "../../shared/AppCard";
import { componentsMap } from "../../shared/form";
export const DynamicForm = (props) => {
const buttonClasses = buttonStyles();
const { defaultValues = {} } = props;
const { handleSubmit, register } = useForm({ defaultValues });
const onSubmit = (userData) => {
props.handleSubmit(userData);
};
return (
<form
id={props.formName}
name={props.formName}
onSubmit={handleSubmit((data) => onSubmit(data))}
>
<AppCard
headline={props.headline}
actionButton={
props.actionButtonText && (
<Button className={buttonClasses.outlined} type="submit">
{props.actionButtonText}
</Button>
)
}
>
<Grid container spacing={2}>
{props.formFields.map((config) => {
const FormComponent = componentsMap.get(config.component);
return (
<Grid key={`form-field-${config.config.name}`} item xs={12}>
<FormComponent {...config.config} register={register} />
</Grid>
);
})}
</Grid>
</AppCard>
</form>
);
};
N.B. The images are the same because the forms will contain the same information, i.e. the same form fields by name.
Entry for first form:
Entry for the second form:
Each form is created with a config like this:
{
component: DynamicForm,
label: "Stepper Label",
config: {
headline: "Form 1",
actionButtonText: "Next",
formName: 'form-name',
defaultValues: defaultConfigObject,
formFields: [
{
component: "AppTextInput",
config: {
label: "Field 1",
name: "field_1",
},
},
{
component: "AppTextInput",
config: {
label: "Field2",
name: "field_2",
},
},
{
component: "AppTextInput",
config: {
label: "Field3",
name: "field_3",
},
},
{
component: "AppTextInput",
config: {
label: "Field4",
name: "field4",
},
},
],
handleSubmit: (formData) => console.log(formData),
},
},
And the active component in the steps is handled like:
import { Button, createStyles, makeStyles, Theme } from "#material-ui/core";
import React, { useContext, useEffect, useState } from "react";
import { StepperContext } from "./StepperProvider";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
buttonsContainer: {
margin: theme.spacing(2),
},
buttons: {
display: "flex",
justifyContent: "space-between",
},
})
);
export const StepPanel = (props) => {
const { activeStep, steps, handleBack, handleNext, isFinalStep } = useContext(
StepperContext
);
const [activeComponent, setActiveComponent] = useState(steps[activeStep]);
const classes = useStyles();
useEffect(() => {
setActiveComponent(steps[activeStep]);
}, [activeStep]);
return (
<div>
<activeComponent.component {...activeComponent.config} />
{
isFinalStep ? (
<div className={classes.buttonsContainer}>
<div className={classes.buttons}>
<Button disabled={activeStep === 0} onClick={handleBack}>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={props.finishFunction}
>
Finish And Submit
</Button>
</div>
</div>
)
:
null
}
</div>
);
};
From your image, every form looks the same. You should try providing a unique key value to your component so React know that each form is different. In this case it can be the step number for example:
<activeComponent.component {...props} key='form-step-1'>