Loop of Path and Sampler in Tone.js - reactjs

I'm trying to create a simple metronome with Tone.js in my React app. But I can't get any sound. What am I doing wrong?
Exercise.ts
import React, { useRef, useEffect, useState } from 'react'
import { exercises } from 'data/exercises.json'
import { Typography, Grid, Button } from '#material-ui/core'
import { useParams } from 'react-router-dom'
import { Sampler, Loop, Part, Transport } from 'tone'
let part: Part
let loop: Loop
export default function ExercisePage() {
const [isLoaded, setIsLoaded] = useState(false)
// find exercise
let { id } = useParams()
let exercise = exercises.find(e => e.id === id)
const sampler = useRef<Sampler | null>(null)
useEffect(() => {
sampler.current = new Sampler(
{ A1: 'click_hi.ogg', B1: 'click_lo.ogg' },
() => {
setIsLoaded(true)
},
`${process.env.PUBLIC_URL}/audio/`
).toMaster()
part = new Part(
(time, note) => {
// #ts-ignore
sampler.current.triggerAttack(note.note, note.time, time, note.velocity)
},
[
{ time: '4n', note: 'A1', velocity: 1 },
{ time: '4n', note: 'B1', velocity: 0.5 },
{ time: '4n', note: 'B1', velocity: 0.5 },
{ time: '4n', note: 'B1', velocity: 0.5 },
]
)
loop = new Loop(() => {
part.start(0)
}, '1m')
Transport.start()
}, [])
const startLoop = () => {
loop.start(0)
}
return (
<Grid container direction="column">
<Grid item>
<Typography variant="h4">Exercise: {id}</Typography>
<Button
variant="contained"
color="secondary"
onClick={() => startLoop()}
>
Start
</Button>
</Grid>
</Grid>
)
}

Move Transport.start() to startLoop function
const startLoop = () => {
Transport.start()
loop.start(0)
}

Related

How to implement react-autosuggest to load suggestions in "input-focus"?

I want to all the suggestions on "input-focus" and when the user further types the text so also the suggestions will appear in react-autosuggest.
import "./styles.css";
import Autosuggest from "react-autosuggest";
import { useState } from "react";
const options = ["Suggestion 1", "Suggestion 2", "Suggestion 3"];
export default function App() {
const [s, setS] = useState(options);
const [x, setX] = useState("Suggestion 1");
const [y, setY] = useState(false);
return (
<div className="App">
<Autosuggest
inputProps={{
value: x,
onChange: (event, { newValue }) => {
setX(newValue);
},
onFocus: () => {
setY(true);
},
onBlur: () => {
setY(false);
}
}}
suggestions={s}
onSuggestionsFetchRequested={({ value }) => {
setS(options.filter((x) => x.includes(value)));
}}
onSuggestionsClearRequested={() => {
setS([]);
}}
renderSuggestion={(text: string) => {
return <>{text}</>;
}}
getSuggestionValue={(a) => {
return a;
}}
alwaysRenderSuggestions={y}
/>
</div>
);
}
Code sandbox here. HTH.

Testing rxjs subscription in react using jest

I am trying to test the below code
Slider.tsx
import React, { useEffect, useState } from "react";
import { ISlide } from "./ISlide";
import styled, { css } from "styled-components";
import { BehaviorSubject, Observable, Subject, timer } from "rxjs";
import { repeatWhen, take, takeUntil, takeWhile, tap } from "rxjs/operators";
interface ISliderProps {
...
}
const SliderWrapper = styled.ul`
...
`;
const SliderItem = styled.li`
...
`;
export const Slider = (props: ISliderProps) => {
const {
slides,
activeSlide = 0,
onActiveItemChange,
itemsPerPage = 1,
delay = 5000,
interval = 5000,
loop = true
} = props;
const [currentActiveSlide, setCurrentActiveSlide] = useState(activeSlide);
const sliderCount = Math.ceil(slides.length / itemsPerPage);
const timerStart$: Observable<null> = new BehaviorSubject(null);
const timerStop$: Observable<null> = new Subject<null>();
const timer$ = timer(delay, interval).pipe(
takeWhile(() => loop),
takeUntil(timerStop$),
repeatWhen(() => timerStart$)
);
useEffect(() => {
const timerSubscription = timer$.subscribe({
next: (i) => {
const nextSlide = i >= sliderCount ? i % sliderCount : i;
onActiveItemChange(nextSlide);
setCurrentActiveSlide(nextSlide);
}
});
return () => timerSubscription.unsubscribe();
}, []);
const handleSliderItemClick = (i: number) => {
setCurrentActiveSlide(i);
timerStop$.next(null);
timer(delay).pipe(
take(1),
tap(timerStart$.next(null))
)
}
return (
<>
<SliderWrapper>
{currentActiveSlide}
{slides.map(({ id }, i) =>
<SliderItem
key={id}
aria-label={"scroll page number " + id}
onClick={() => handleSliderItemClick(i)}
active={i === currentActiveSlide}
/>)}
</SliderWrapper>
</>
);
};
Below is my test
import React from "react";
import { Slider } from "../../../components/slider/Slider";
import renderer, { act } from "react-test-renderer";
import { ISlide } from "../../../components/slider/ISlide";
it("renders without crashing", done => {
const slides: ISlide[] = [
{ id: 1 }, { id: 2 }, { id: 3 },
{ id: 4 }, { id: 5 }, { id: 6 },
{ id: 7 }, { id: 8 }
];
act(() => {
const component = renderer.create(<Slider itemsPerPage={3} slides={slides} activeSlide={0} />);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
done();
});
});
I have called done but the test keeps returning a warning
Jest did not exit one second after the test run has completed.
As expected the problem is in the subscription
● Timeout
67 |
68 | useEffect(() => {
> 69 | const timerSubscription = timer$.subscribe({
| ^
70 | next: (i) => {
71 | const nextSlide = i >= sliderCount ? i % sliderCount : i;
72 | onActiveItemChange(nextSlide);
How do I unsubscribe from this subscription?

React Datepicker for rendering list is always one day behind

I am building a ToDoList with React and a Django rest Api but I am also using a Datepicker to render all the tasks for the day by the date created. So if I choose the 12.06 the api calls all the tasks for that day and displays them. But every time I change the date to show the list for the day the list for that day is not shown.
Only if i change it to another day the list of the previous date is shown. For example I choose the 12.06 nothing appears but then switch to the 11.06 the tasks for the 12th are being rendered. If I console log the dates in the Datepicker it shows the right day but if I do it inside the handleDateChange I hangs behind.
import 'date-fns'
import Grid from '#material-ui/core/Grid'
import DateFnsUtils from '#date-io/date-fns'
import{
MuiPickersUtilsProvider,
KeyboardTimePicker,
KeyboardDatePicker
} from '#material-ui/pickers'
import TodoForm from '../ToDo/TodoForm'
function Datepicker() {
const initialDate = new Date(Date.now())
const [selectDate, setSelectDate] = useState(
`${initialDate.getFullYear()}-${initialDate.getMonth()+1}-${initialDate.getDate()}`
)
console.log("In App selected Date = ", selectDate)
const handleDateChange = (date) =>{
// setSelectDate(date)
setSelectDate(`${date.getFullYear()}-${date.getMonth()+1}-${date.getDate()}`)
console.log("TimePicker selected Date = ", selectDate)
}
return (
<div>
<div>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<Grid container justify='space-around'>
<KeyboardDatePicker
disableToolbar
varient='inline'
format='MM/dd/yy'
margin='normal'
id='date-picker'
label='Pick your Date'
value={selectDate}
onChange={handleDateChange}
KeyboradButtonProps={{
'aris-label': 'change date'
}}
/>
</Grid>
</MuiPickersUtilsProvider>
</div>
<div>
<TodoForm date={selectDate}/>
</div>
</div>
)
}
export default Datepicker
import React, { Component, useState,useEffect } from 'react'
import Select from 'react-select'
import { apiTaskCreate } from './lookup'
import { ActionBtn } from './buttons'
import TodoList from './TodoList'
function TodoForm(props) {
const [newTasks, setNewTasks] = useState([])
const [taskname, SetTaskname] = useState('')
const [Importants, setImportants] = useState({})
const [TimeComplete, setTimeComplete] = useState({})
const date_of_task = props.date
const handleChange = e => {
SetTaskname(e.target.value)
}
function onChangeImportants(value){
setImportants(value.value)
}
function onChangeTimeComplete(value){
setTimeComplete(value.value)
}
const handleSubmit = e =>{
e.preventDefault()
SetTaskname('')
let tempNewTasks = [...newTasks]
apiTaskCreate(taskname,Importants,TimeComplete,date_of_task,(response, status)=>{
// console.log(response, status)
if (status === 201){
tempNewTasks.unshift(response)
setNewTasks(tempNewTasks)
} else {
console.log(response)
alert("an error accourd")
}
})
}
const Importants_options = [
{ value: '1', label: 1 },
{ value: '2', label: 2 },
{ value: '3', label: 3 },
{ value: '4', label: 4 },
{ value: '5', label: 5 },
]
const Time_options = [
{ value: '1', label: 30 },
{ value: '2', label: 60 },
{ value: '3', label: 90 },
{ value: '4', label: 120 },
{ value: '5', label: 150 },
{ value: '6', label: 180 },
]
return (
<div className={props.className}>
<div className='col-11 mb-3'>
<form className='todo-form mb-3' onSubmit={handleSubmit}>
<input type='text' value={taskname} placeholder='Task Name'
name='task_name' className='todo-input' onChange={handleChange}></input>
<Select onChange={onChangeImportants} options={Importants_options} placeholder="Importants Score"/>
<Select onChange={onChangeTimeComplete} options={Time_options} placeholder="Time to complete"/>
<button className='btn btn-primary'>Submit</button>
<ActionBtn action={{type: 'optimize', display:"Optimize"}}/>
</form>
</div>
<div className='container'>
<TodoList newTasks={newTasks} {...props}/>
</div>
</div>
)
}
export default TodoForm
import React, {useState, useEffect} from 'react'
import { apiTaskList } from './lookup'
import Task from './Task'
function TodoList(props) {
const [tasksInit, setTasksInit] = useState([])
const [tasks, setTasks] = useState([])
const [tasksDidSet, setTasksDidSet] = useState(false)
// const initialDate = new Date(Date.now())
function join(t, a, s) {
function format(m) {
let f = new Intl.DateTimeFormat('en', m);
return f.format(t);
}
return a.map(format).join(s);
}
let a = [{year: 'numeric'},{month: 'numeric'},{day: 'numeric'}];
const initialDate = join(new Date, a, '-');
const [date, setDate] = useState(initialDate)
// `${initialDate.getFullYear()}-${initialDate.getMonth()+1}-${initialDate.getDate()}`
useEffect( () =>{
const final = [...props.newTasks].concat(tasksInit);
setTasks(tasks => {
if (final.length !== tasks.length) {
return final;
}
return tasks
});
}, [props.newTasks, tasksInit])
useEffect(() => {
if (tasksDidSet === false) {
const handleTasksListLookup = (response, status) => {
if (status === 200) {
setTasksInit(response);
setDate(props.date);
}
}
apiTaskList("admin", date ,handleTasksListLookup)
}
}, [setTasksInit, props.date, tasksDidSet])
return tasks.map((item, index)=>{
return <Task task={item} className='d-flex p-2 justify-content-between border bg-white text-dark' key={`${index}-${item.id}`}/>
})
}
export default TodoList;
import { backendlookup } from "../lookup/lookup";
export function apiTaskCreate(newTask_Name,newImportans_Score,newTime_to_complete,new_date_of_task,callback) {
backendlookup('POST', 'create',callback, {
Task_name: newTask_Name,
Importants_Score: newImportans_Score,
Time_to_Finish: newTime_to_complete,
date_of_task: new_date_of_task,
})
}
export function apiTaskList(username,date,callback) {
let endpoint = 'tasks'
if (date){
endpoint = `tasks?username=${username}&date=${date}`
}
backendlookup('GET', endpoint ,callback)
}
export function apiPartyActionOptimize(action,callback) {
backendlookup('POST', 'action-optimize',callback, {action:action})

React calls api thousands of times inside a useEffect

I am building a ToDoList with React and a Django rest Api but I am also using a Datepicker to render all the tasks for the day by the date created. But every time the site loads the api is called thousands of times. The goal would be to only render the tasks for the specific day when the date is changed or a new task for the day is added and not call the backend constalnly the problem seams to lay at the ToDoList because evertwhere else the props.date is only called once and not in a loop.
import React, { useState } from 'react'
import 'date-fns'
import Grid from '#material-ui/core/Grid'
import DateFnsUtils from '#date-io/date-fns'
import{
MuiPickersUtilsProvider,
KeyboardTimePicker,
KeyboardDatePicker
} from '#material-ui/pickers'
import TodoForm from '../ToDo/TodoForm'
function Datepicker() {
const initialDate = new Date(Date.now())
const [selectDate, setSelectDate] = useState(
`${initialDate.getFullYear()}-${initialDate.getMonth()+1}-${initialDate.getDate()}`
)
const handleDateChange = (date) =>{
setSelectDate(`${date.getFullYear()}-${date.getMonth()+1}-${date.getDate()}`)
}
return (
<div>
<div>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<Grid container justify='space-around'>
<KeyboardDatePicker
disableToolbar
varient='inline'
format='MM/dd/yy'
margin='normal'
id='date-picker'
label='Pick your Date'
value={selectDate}
onChange={handleDateChange}
KeyboradButtonProps={{
'aris-label': 'change date'
}}
/>
</Grid>
</MuiPickersUtilsProvider>
</div>
<div>
<TodoForm date={selectDate}/>
</div>
</div>
)
}
export default Datepicker
import React, { Component, useState,useEffect } from 'react'
import Select from 'react-select'
import { apiTaskCreate } from './lookup'
import { ActionBtn } from './buttons'
import TodoList from './TodoList'
function TodoForm(props) {
const [newTasks, setNewTasks] = useState([])
const [taskname, SetTaskname] = useState('')
const [Importants, setImportants] = useState({})
const [TimeComplete, setTimeComplete] = useState({})
const handleChange = e => {
SetTaskname(e.target.value)
}
function onChangeImportants(value){
setImportants(value.value)
}
function onChangeTimeComplete(value){
setTimeComplete(value.value)
}
const handleSubmit = e =>{
e.preventDefault()
SetTaskname('')
let tempNewTasks = [...newTasks]
apiTaskCreate(taskname,Importants,TimeComplete,(response, status)=>{
// console.log(response, status)
if (status === 201){
tempNewTasks.unshift(response)
setNewTasks(tempNewTasks)
} else {
console.log(response)
alert("an error accourd")
}
})
}
const Importants_options = [
{ value: '1', label: 1 },
{ value: '2', label: 2 },
{ value: '3', label: 3 },
{ value: '4', label: 4 },
{ value: '5', label: 5 },
]
const Time_options = [
{ value: '1', label: 30 },
{ value: '2', label: 60 },
{ value: '3', label: 90 },
{ value: '4', label: 120 },
{ value: '5', label: 150 },
{ value: '6', label: 180 },
]
return (
<div className={props.className}>
<div className='col-11 mb-3'>
<form className='todo-form mb-3' onSubmit={handleSubmit}>
<input type='text' value={taskname} placeholder='Task Name'
name='task_name' className='todo-input' onChange={handleChange}></input>
<Select onChange={onChangeImportants} options={Importants_options} placeholder="Importants Score"/>
<Select onChange={onChangeTimeComplete} options={Time_options} placeholder="Time to complete"/>
<button className='btn btn-primary'>Submit</button>
<ActionBtn action={{type: 'optimize', display:"Optimize"}}/>
</form>
</div>
<div className='container'>
<TodoList newTasks={newTasks} {...props}/>
</div>
</div>
)
}
export default TodoForm
import React, {useState, useEffect} from 'react'
import { apiTaskList } from './lookup'
import Task from './Task'
function TodoList(props) {
const [tasksInit, setTasksInit] = useState([])
const [tasks, setTasks] = useState([])
const [tasksDidSet, setTasksDidSet] = useState(false)
const initialDate = new Date(Date.now())
const [date, setDate] = useState("2021-04-12")
// `${initialDate.getFullYear()}-${initialDate.getMonth()+1}-${initialDate.getDate()}`
useEffect( () =>{
const final = [...props.newTasks].concat(tasksInit)
if (final.length !== tasks.length) {
setTasks(final)
}
}, [props.newTasks, tasks, tasksInit])
useEffect(() => {
if (tasksDidSet === false) {
const handleTasksListLookup = (response, status) => {
if (status === 200) {
setTasksInit(response)
setDate(props.date)
console.log(date)
}
}
apiTaskList("admin", date ,handleTasksListLookup)
}
}, [tasksInit, setTasksDidSet, setTasksDidSet, date])
return tasks.map((item, index)=>{
return <Task task={item} className='d-flex p-2 justify-content-between border bg-white text-dark' key={`${index}-${item.id}`}/>
})
}
export default TodoList;
import { backendlookup } from "../lookup/lookup";
export function apiTaskCreate(newTask_Name,newImportans_Score,newTime_to_complete,callback) {
backendlookup('POST', 'create',callback, {
Task_name: newTask_Name,
Importants_Score: newImportans_Score,
Time_to_Finish: newTime_to_complete,
})
}
export function apiTaskList(username,date,callback) {
let endpoint = 'tasks'
if (date){
endpoint = `tasks?username=${username}&date=${date}`
}
backendlookup('GET', endpoint ,callback)
}
export function apiPartyActionOptimize(action,callback) {
backendlookup('POST', 'action-optimize',callback, {action:action})
You are adding a dependency to useEffect which itself is being updated inside it. If you do so, your useEffect execution will go into an infinite loop.
You can make use of functional version of setState to update the tasks state.
Your updated code will look as follows
useEffect( () =>{
const final = [...props.newTasks].concat(tasksInit);
setTasks(tasks => {
if (final.length !== tasks.length) {
return final;
}
return tasks
});
}, [props.newTasks, tasksInit])
useEffect(() => {
if (tasksDidSet === false) {
const handleTasksListLookup = (response, status) => {
if (status === 200) {
setTasksInit(response);
setDate(props.date);
}
}
apiTaskList("admin", date ,handleTasksListLookup)
}
}, [setTasksInit, props.date, tasksDidSet])
When you call the setTasks method inside the useEffect function it updates the tasks value and because you have the tasks variable as one of the dependencies in the useEffect callback, the API gets called indefinitely. Remove tasks from the useEffect dependency and it should work.

GSAP animate elements in an array fetched from the server

I would like to animate each element in an array with TweenMax.staggerFrom. At the moment I created this working sample
import React, { useRef, useEffect createRef } from 'react';
import { TweenMax } from 'gsap';
const AnimateView = () => {
const data = [
{ title: 'Title 1', value: 1000 },
{ title: 'Title 2', value: 1100 },
{ title: 'Title 3', value: 1200 },
];
const elementsRef = useRef(data.map(() => createRef()));
useEffect(() => {
const elements = elementsRef.current.map(el => el.current);
TweenMax.staggerFrom(elements, 1, { scale: 0 }, 0.3);
}, []);
return (
<>
{data.map((item, index) => (
<div ref={elementsRef.current[index]}>{item.title}</div>
))}
</>
);
};
export default AnimateView;
The difference is that I want data array to be fetched from the server. I cannot figure out why in elementsRef I'm getting no attached refs. Below you can check what I wanted to achieve.
import React, { useRef, useEffect createRef } from 'react';
import { TweenMax } from 'gsap';
import { connect } from 'react-redux';
import { fetchData } from 'actions';
const AnimateView = ({ combineFetching, income }) => {
const elementsRef = useRef(income.length && income.map(() => createRef()));
useEffect(() => {
const elements = elementsRef.current.map(el => el.current);
TweenMax.staggerFrom(elements, 1, { scale: 0 }, 0.3);
}, []);
return (
<>
<button onClick={fetchData}>Click</button>
{income.map((item, index) => (
<div ref={elementsRef.current[index]}>{item.title}</div>
))}
</>
);
};
const mapDispatchToProps = state => ({
income: state.budget.income,
});
export default connect(
mapDispatchToProps,
{ fetchData },
)(AnimateView);
On Click I want to fetch data from database and animate each element in the array.

Resources