Sorry if the title is a bit confusing. Basically I want to play a sound if a user clicks a key, and if they click again it will play another sound.
The setup could look like.
import useSound from 'use-sound';
const Home = () => {
const soundList = [assets.sounds.click1,assets.sounds.click2]
const [play] = useSound(soundList);// this seems to take one argument.
function onUserInputChange(e){
play(// random choice from soundList)
}
}
How might I be able to pass and argument into play to play audio?
You can pass from the parent url of the sound to the child and modify parent's state after click:
import useSound from 'use-sound';
import { useState } from 'react';
const soundList = [assets.sounds.click1, assets.sounds.click2];
const Home = () => {
const [soundToPlay, setSoundToPlay] = useState(soundList[0]);
const onPlay = () => {
setSoundToPlay(soundList[Math.round(Math.random() * soundList.length)]);
};
return <PlayComponent soundUrl={soundToPlay} onPlay={onPlay} />;
};
const PlayComponent = ({ soundUrl, onPlay }) => {
const [play] = useSound(soundUrl);
function onUserInputChange(e) {
play();
onPlay();
}
return <button onClick={onUserInputChange}>Play</button>;
};
*NOTE
I guess you wanted to put assets.sounds.click2 instead of assets.sounds.click1 as a second array item
Related
I'm pretty new to coding so I hope my question won't sound too ridiculous. I've spent days to figure something out but didn't find anything.
So I have a react page with the main component that is my "planning container".
In this component, I have a child component that represents a whole month. it's a table with a line for each people and my team, and a td that represents each day of the month.
This child is stored in a variable array that is stored in the state.
When I click on a button, I create another child (for the next month), that I will push into the array stored in the state.
I have a variable called "refresh" that just setRefresh(!refresh). This variable is passed to the childs components and I put this variable in the useEffect [] that will trigger a re-render.
=> The problem is that it doesn't re-render at all.
So, just for you to know, I really need to be able to "append" a whole month at each click on the button. I really need this "view" to be able to work.
I will paste some code to make you understand the main idea.
here is the parent :
import React, {useEffect, useState} from "react";
import PlanningMonthBuild from "./PlanningMonthBuild";
import './Planning.css';
import "react-datepicker/dist/react-datepicker.css";
const PlanningBuild = () => {
const [startForNextMonth, setStartForNextMonth] = useState(new Date());
const [inputList, setInputList] = useState([]);
const [refresh, setRefresh] = useState(false);
useEffect(() => {
initPlanning();
}, []);
const handleRefresh = () => {
setRefresh(!refresh);
}
const initPlanning = () => {
let date = new Date(startForNextMonth.getFullYear(), startForNextMonth.getMonth(), 1);
let newPlanning = [...inputList];
newPlanning.splice(0, 0,
<PlanningMonthBuild
key={'current' + new Date().toISOString()}
startDate={date}
refresh={refresh}
/>
);
let date2 = new Date(startForNextMonth.getFullYear(), startForNextMonth.getMonth() + 1, 1);
date2.setMonth(date.getMonth() + 1);
setStartForNextMonth(date2);
setInputList(newPlanning);
};
const addOneMonthNext = () => {
setInputList(inputList.concat(
<PlanningMonthBuild
key={new Date().toISOString()}
startDate={startForNextMonth}
refresh={refresh}
/>
));
let date = startForNextMonth;
date.setDate(1);
date.setMonth(date.getMonth() + 1);
setStartForNextMonth(date);
};
return (
<main>
<div className="refresh-div" onClick={handleRefresh}><i className="fa fa-refresh" aria-hidden="true"></i></div>
<button className="myButton" onClick={addOneMonthNext}>Add Next Month</button>
<div id="insideDiv">
{inputList}
</div>
</main>
);
};
export default PlanningBuild;
And here is the child (that build each month) :
import React, {useEffect, useState} from "react";
import useAxiosPrivate from "../hooks/useAxiosPrivate";
import {getFirstDayOfMonth} from "../utils/dates";
const PlanningMonthBuild = ({ startDate, refresh }) => {
const [dateDebut] = useState(startDate);
const [jsListe, setJsListe] = useState([]);
const axiosPrivate = useAxiosPrivate();
useEffect(() => {
let isMounted = true;
const controller = new AbortController();
try {
axiosPrivate.get('api/planning/team?start=' + getFirstDayOfMonth(dateDebut.getFullYear(), dateDebut.getMonth()), {
signal: controller.signal
}).then(response => {
if (isMounted) {
const newListeJS = response.data;
setJsListe(newListeJS);
}
});
} catch (err) {}
return () => {
isMounted = false;
controller.abort();
}
}, [refresh])
return (jsListe.map((day)=> {
///.... building a table(tr,td...)
}))
}
export default PlanningMonthBuild;
So, when I put my child component directly into the parent return, it works when I click on refresh button (if my workmate updates something for example...), it will update my table but if I store my child component in the array in "inputList" state it doesn't...
I hope you can understand what I mean.
Thanks in advance for any help.
I'm working on a new major release for react-xarrows, and I came up with some messy situation.
It's not going to be simple to explain, so let's start with visualization:
consider the next example - 2 draggable boxes with an arrow drawn between them, and a wrapping context around them.
focused code:
<Xwrapper>
<DraggableBox box={box} />
<DraggableBox box={box2} />
<Xarrow start={'box1'} end={'box2'} {...xarrowProps} />
</Xwrapper>
Xwrapper is the context, DraggableBox and Xarrow are, well, you can guess.
My goal
I want to trigger a render on the arrow, and solely on the arrow, whenever one of the connected boxes renders.
My approach
I want to be able to rerender the arrow from the boxes, so I have to consume 'rerender arrow'(let's call it updateXarrow) function on the boxes, we can use a context and a useContext hook on the boxes to get this function.
I will call XelemContext to the boxes context.
also, I need to consume useContext on Xarrow because I want to cause a render on the arrow whenever I decide.
this must be 2 different contexts(so I could render xarrow solely). one on the boxes to consume 'updateXarrow', and a different context consumed on Xarrow to trigger the reredner.
so how can I pass this function from one context to another? well, I can't without making an infinite loop(or maybe I can but could not figure it out), so I used a local top-level object called updateRef.
// define a global object
const updateRef = { func: null };
const XarrowProvider = ({ children }) => {
// define updateXarrow here
...
// assign to updateRef.func
updateRef.func = updateXarrow;
return <XarrowContext.Provider value={updateXarrow}>{children}</XarrowContext.Provider>;
};
//now updateRef.func is defined because XelemProvider defined later
const XelemProvider = ({ children }) => {
return <XelemContext.Provider value={updateRef.func}>{children}</XelemContext.Provider>;
};
the thing is, that this object is not managed by react, and also, i will need to handle cases where there is multiple instances of Xwrapper, and I'm leaving the realm of React, so i have 2 main questions:
there is a better approach? maybe I can someone achieve my goal without going crazy?
if there is no better option, is this dangerous? I don't want to release a code that will break on edge cases on my lib consumer's apps.
Code
DraggableBox
const DraggableBox = ({ box }) => {
console.log('DraggableBox render', box.id);
const handleDrag = () => {
console.log('onDrag');
updateXarrow();
};
const updateXarrow = useXarrow();
return (
<Draggable onDrag={handleDrag} onStop={handleDrag}>
<div id={box.id} style={{ ...boxStyle, position: 'absolute', left: box.x, top: box.y }}>
{box.id}
</div>
</Draggable>
);
};
useXarrow
import React, { useContext, useEffect, useLayoutEffect, useRef, useState } from 'react';
import { XelemContext } from './Xwrapper';
const useXarrow = () => {
const [, setRender] = useState({});
const reRender = () => setRender({});
const updateXarrow = useContext(XelemContext);
useLayoutEffect(() => {
updateXarrow();
});
return reRender;
};
export default useXarrow;
Xwrapper
import React, { useState } from 'react';
export const XelemContext = React.createContext(null as () => void);
export const XarrowContext = React.createContext(null as () => void);
const updateRef = { func: null };
const XarrowProvider = ({ children }) => {
console.log('XarrowProvider');
const [, setRender] = useState({});
const updateXarrow = () => setRender({});
updateRef.func = updateXarrow;
return <XarrowContext.Provider value={updateXarrow}>{children}</XarrowContext.Provider>;
};
const XelemProvider = ({ children }) => {
console.log('XelemProvider');
return <XelemContext.Provider value={updateRef.func}>{children}</XelemContext.Provider>;
};
const Xwrapper = ({ children }) => {
console.log('Xwrapper');
return (
<XarrowProvider>
<XelemProvider>{children}</XelemProvider>
</XarrowProvider>
);
};
export default Xwrapper;
const Xarrow: React.FC<xarrowPropsType> = (props: xarrowPropsType) => {
useContext(XarrowContext);
const svgRef = useRef(null);
....(more 1100 lines of code)
logs
I left some logs.
on drag event of a single box you will get:
onDrag
DraggableBox render box2
XarrowProvider
xarrow
Note
currently, this is working as expected.
Update
after many hours of testing, this seems to work perfectly fine. I manage my own object that remember the update function for each Xwrapper instance, and this breaks the dependency between the 2 contexts. I will leave this post in case someone else will also come across this issue.
Update (bad one)
this architecture breaks on react-trees with <React.StrictMode>...</React.StrictMode> :cry:
any idea why? any other ideas ?
just in case someone would need something similar: here's a version that will work even with react strictmode(basically being rellyed of effect which called once and not renders):
import React, { FC, useEffect, useRef, useState } from 'react';
export const XelemContext = React.createContext(null as () => void);
export const XarrowContext = React.createContext(null as () => void);
// will hold a object of ids:references to updateXarrow functions of different Xwrapper instances over time
const updateRef = {};
let updateRefCount = 0;
const XarrowProvider: FC<{ instanceCount: React.MutableRefObject<number> }> = ({ children, instanceCount }) => {
const [, setRender] = useState({});
const updateXarrow = () => setRender({});
useEffect(() => {
instanceCount.current = updateRefCount; // so this instance would know what is id
updateRef[instanceCount.current] = updateXarrow;
}, []);
// log('XarrowProvider', updateRefCount);
return <XarrowContext.Provider value={updateXarrow}>{children}</XarrowContext.Provider>;
};
// renders only once and should always provide the right update function
const XelemProvider = ({ children, instanceCount }) => {
return <XelemContext.Provider value={updateRef[instanceCount.current]}>{children}</XelemContext.Provider>;
};
const Xwrapper = ({ children }) => {
console.log('wrapper here!');
const instanceCount = useRef(updateRefCount);
const [, setRender] = useState({});
useEffect(() => {
updateRefCount++;
setRender({});
return () => {
delete updateRef[instanceCount.current];
};
}, []);
return (
<XelemProvider instanceCount={instanceCount}>
<XarrowProvider instanceCount={instanceCount}>{children}</XarrowProvider>
</XelemProvider>
);
};
export default Xwrapper;
I am using React. Tell me how to make it beautifully (right!). On the page, I have two almost identical sections:
And I'm trying to follow the rule, keep containers and components separate. There is a wrapper in which there is one api request to receive a picture (hereinafter it is transmitted as a props) for a specific section, it is rendered in this way:
It turns out that this wrapper is (almost) the same:
I understand that this can be done correctly, but something does not work. I am confused by the fact that it is necessary to return two different components from the wrapper, where the api request to receive a picture goes. (I was looking towards hoc, but I haven't figured out how to use it myself). Thank you in advance.
I did it all the same through hoc. Here is the component itself:
function LoadingSnapshotHOC(Component) {
const NewComponent = (props) => {
const isMounted = useIsMounted();
const state = useSelector(({ dateParams }) => {
const { currentPage } = props;
return {
selectedTimeLabel: dateParams?.[currentPage].selectedTimePeriod.label,
compareTimeLabel: dateParams?.[currentPage].compareTimePeriod.label,
};
});
const [snapshot, setSnapshot] = useState("");
const updateSnapshot = async (deviceID) => {
const img = await getSnapshot(deviceID);
img.onload = () => {
if (isMounted.current) {
setSnapshot(img);
}
};
};
useEffect(() => {
if (props.deviceID) updateSnapshot(props.deviceID);
}, [props.deviceID]);
return (
<Component
{...props}
snapshot={snapshot}
selectedTimeLabel={state.selectedTimeLabel}
compareTimeLabel={state.compareTimeLabel}
/>
);
};
return NewComponent;
}
export default LoadingSnapshotHOC;
Next, I wrapped my components:
function HeatMapSnapshot({...}) {
...
}
export default LoadingSnapshotHOC(HeatMapSnapshot);
and
function TrafficFlowSnapshot({...}) {
...
}
export default LoadingSnapshotHOC(TrafficFlowSnapshot);
And their render. Thank you all for your attention!
I know this has been asked a million times, I have looked at the other answers, but for some reason I just don't get it. Sorry for the repeat. I've been working on this for DAYS
TLDR: How do I correctly pass an array of objects into state?
I have created a React Hook, which is a button, that has an OnClick that fires off an action and reducer.
The reducer logic takes an array taken from state, that splits it into two arrays, and then returns these two arrays. Basically, I have a pool of player objects, and then split them into two teams using very basic team-balancing logic.
When I was writing the reducer logic, I tested it in a separate javascript file and when passing an initial state type object into it I was getting the correct output. (an array of objects)
The idea is that a separate component then lists the name property of each player.
When I click the button I get the error in the title and my state is wiped except for the three states I change. MY redux devtool shows both arrays of players just being [null].
Button Hook Component
import { useDispatch } from "react-redux";
const GenerateTeams = () => {
const dispatch = useDispatch();
function passTeamsIntoState() { //this is the problematic one
dispatch ({
type: "GENERATE_TEAMS",
})
}
const goToPage = () => dispatch({ //this one changes state fine
type: "NAV_TO_GENERATE"
});
const onClick = () => {
passTeamsIntoState();
goToPage();
}
return (
<>
<button
className="doButton"
onClick= { onClick }
>
Generate Teams
</button>
</>
)
}
export default GenerateTeams;
The reducer imports a bunch of functions I've put in separate folders, but it returns when I would have thought it should, ...state, then the props we want changed.
Reducer logic
import setTeamMembers from './setTeamMembers';
import selectedPlayers from './selectedPlayers';
const GenerateTeams = (state) => {
let players = [...state.players];
let teams_size = state.teams_size;
// Select required players and generate two balanced teams
let playerPool = selectedPlayers(teams_size, players);
return setTeamMembers(playerPool);
}
let teams = GenerateTeams();
console.log(teams);
let team_one = [...teams.team_one_players];
let team_two = [...teams.team_two_players];
let setTeamsReducer = (state) => {
return {
...state,
team_one_players: team_one,
team_two_players: team_two,
}
}
export default setTeamsReducer;
The component that is meant to list the players is a Redux class based component:
Component that's trying to read the objects
const PlayersCardT1 = ({team_one_players}) => {
return (
<ul>
{
team_one_players.map(player => (
<li name = { player.name } key = { player } />
))
}
</ul>
)
}
export default PlayersCardT1;
and its index.js file:
import { connect } from 'react-redux';
import Players from './PlayersCardT1';
const mapStateToProps = (state) => {
return {
team_one_players: state.team_one_players,
};
};
export default connect(mapStateToProps)(Players);
Also, Happy new year folks!
I am using this https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html to access my navigation from any source, my file look as follow:
import { createRef } from 'react';
export const navigationRef = createRef();
export function navigate(name, params) {
return navigationRef.current?.navigate(name, params);
}
export function goBack() {
return navigationRef.current?.goBack();
}
export function getRootState() {
return navigationRef.current?.getRootState();
}
This is perfect for my #navigation/drawer, which is outside my stack navigation.
Only one problem the last method is not synchronized and I want to have an active state on my item menu that is the current route.
How is that possible with react navigation 5?
I am using the following approach to get the current route name in react-navigation v5.
https://reactnavigation.org/docs/navigation-prop/#dangerouslygetstate
const {index, routes} = this.props.navigation.dangerouslyGetState();
const currentRoute = routes[index].name;
console.log('current screen', currentRoute);
The NavigationContainer has onStateChange prop, useful for this case, check react-navigation docs Screen Tracking for analytics and if you need access without navigation prop see Navigating without the navigation prop
I share the code to get only active routeName
function App(){
const routeNameRef = React.useRef();
// Gets the current screen from navigation state
const getActiveRouteName = (state)=> {
const route = state.routes[state?.index || 0];
if (route.state) {
// Dive into nested navigators
return getActiveRouteName(route.state);
}
return route.name;
};
return (<NavigationContainer
onStateChange={(state) => {
if (!state) return;
//#ts-ignore
routeNameRef.current = getActiveRouteName(state);
}}
>
...
</NavigationContainer>)
}
If you want to know the current screen from a component you can also use this:
export const HomeScreen = ({ navigation, route }) => {
console.log(route.name);
return (
{...}
);
};
It is possible to get this from the navigationRef attached to the navigation container. Where navigationRef is a ref.
export const navigationRef = React.createRef()
and
<NavigationContainer
ref={navigationRef}
>
<Navigator />
</NavigationContainer>
Then use: const currentRouteName = navigationRef.current.getCurrentRoute().name to get the current route name.
Alternatively in a functional component you can useRef const navigationRef = React.useRef()
There is a util function called getFocusedRouteNameFromRoute(route) which the docs recommends.
BUT - it seems its working only for child screen, so I defined the following function to get the active route name:
const getCurrentRouteName = (navigation, route) => {
if (route.state)
return getFocusedRouteNameFromRoute(route);
return route.name;
};
This works for me. In navigationRef.js
let navigator;
export const setNavigator = (nav) => {
navigator = nav;
};
export const getCurrentRoute = () => {
const route = navigator.getCurrentRoute();
return route.name;
};
This can be referred from any source like this
import { getCurrentRoute } from '../navigationRef';
const currentScene = getCurrentRoute();