I'm trying to call function once after component created with react.. I'm new in to this, so hope you can help me to understand. I tryied to put onLoad event in component creation, but it doesn't work. I tried to just put function call, but it's being called in the circle, but i need it to be called once - when component will finish to load.
Below is the function i got to create component and i want function 'handleClick' also to be called when component will be loaded.
function BadgeSample(props) {
const {
type,
defaultValue,
className,
style,
value,
bootstrapStyle,
clickable,
onClickAction,
getRef
} = props;
const [disabled, setDisabled] = React.useState(false);
const [pasuedd, setPasued] = React.useState(false);
const [highlightSection, setHighlightSection] = React.useState({
from: 0,
to: 0
});
const synth = window.speechSynthesis;
let utterance;
const handleClick = () => {
if (!synth) {
console.error("no tts");
return;
}
utterance = new SpeechSynthesisUtterance(value || defaultValue);
utterance.addEventListener("start", () => setDisabled(true));
utterance.addEventListener("end", () => setDisabled(false));
utterance.addEventListener("boundary", ({
charIndex,
charLength
}) => {
setHighlightSection({
from: charIndex,
to: charIndex + charLength
});
});
synth.speak(utterance);
};
const stoped = () => {
synth.cancel(utterance);
};
const pasued = () => {
setPasued(true);
synth.pause(utterance);
};
const resumed = () => {
setPasued(false);
synth.resume(utterance);
};
//handleClick(); - here it will work, but it will repeat itself in the loop and never stopped
return React.createElement("div", {
className: "App",
onClick: onClickAction,
onLoad: handleClick, // - here it doesn't work..
ref: getRef,
style: style
}, React.createElement(HighlightedText, _extends$sj({
text: value || defaultValue
}, highlightSection)), React.createElement("button", {
className: disabled ? "stopbtn" : "playbtn",
onClick: disabled ? stoped : handleClick
}, disabled ? React.createElement("div", null, React.createElement(StopCircleFill, null), "Stop") : React.createElement("div", null, React.createElement(PlayCircleFill, null), "Listen")), disabled ? React.createElement("button", {
className: pasuedd ? "playbtn" : "pausebtn",
onClick: pasuedd ? resumed : pasued
}, pasuedd ? React.createElement("div", null, React.createElement(PlayCircleFill, null), "Resume") : React.createElement("div", null, React.createElement(PauseCircleFill, null), "Pause")) : "");
}
I try to make one call of function after page with component will be loaded.. But it doesn't work.
you can use useEffect.
its the most efficiant way that i know and it's very comfortable to use.
its a function that you can decide when you call it, and you have an option to call it just once when your website renders the first time.
here's an example:
import React, { useEffect } from 'react';
const YourComponent = () => {
useEffect(() => {
// your function here
}, []);
return (
// your component's content and so... );
};
and if you want it to be called when one of you states changes you can do that too.
I highly recommend you read about it and use it for your projects.
hope it helps :)
Related
I have a component which lifts its state and the state modifier to the parent:
export default function DamageInput({ data, onChange }){
const [qty, setQty] = useState(data?.qty || null);
const [selectedSize, setSelectedSize] = useState(data?.size || null);
const [selectedSuppl, setSelectedSuppl] = useState(data?.suppl || []);
useEffect(() => {
if(!selectedSize) onChange(null);
else {
const damageObject = {
qty: qty,
size: selectedSize,
suppl: selectedSuppl
}
onChange(damageObject);
}
}, [selectedSize, qty, selectedSuppl, onChange]);
...
The parent which consumes the component:
<DamageInput onChange={(damageObject) => handleDamageChange('bonnet', damageObject)} data={data.damage.bonnet} />
And the handler function:
const handleDamageChange = (panel, damageObject) => {
if(!damageObject){
if(data.damage[panel]){
setData(prevState => {
const damageCopy = {...prevState.damage};
delete damageCopy[panel];
return {...prevState, damage: damageCopy};
});
}
}
else setData(prevState => ({...prevState, damage: {...prevState.damage, [panel]: damageObject}}));
};
The problem is that as soon as the onChange function called with the damageObject parameter, it causes an infinite loop in the first useEffect. I think the problem is that i had to pass the onChange function as a dependency and it changes every time when rerenders because i pass an "anonymus function" with a parameter to the onChange event. Im stuck for hours now and couldnt find the answer. Thank you
The useEffect doesn't fire on first render, but when I save the file (ctrl+s), the state updates and the results can be seen.
What I want to do is, when I'm in GameScreen, I tap on an ICON which takes me to WalletScreen, from there I can select some items/gifts (attachedGifts - in context) and after finalising I go back to previous screen i.e. GameScreen with gifts attached (attachedGifts!==null), now again when I tap ICON and go to WalletScreen it should show me the gifts that were attached so that I could un-attach them or update selection (this is being done in the useEffect below in WalletScreen), but the issue is, although my attachedGifts state is updating, the useEffect in WalletScreen does not fire immediately when navigated, when I hit ctrl+s to save the file, then I can see my selected/attached gifts in WalletScreen.
code:
const Main = () => {
return (
<GiftsProvider>
<Stack.Screen name='WalletScreen' component={WalletScreen} />
<Stack.Screen name='GameScreen' component={GameScreen} />
</GiftsProvider>
)
};
const GameScreen = () => {
const { attachedGifts } = useGifts(); //coming from context - GiftsProvider
console.log('attached gifts: ', attachedGifts);
return ...
};
const WalletScreen = () => {
const { attachedGifts } = useGifts();
useEffect(() => { // does not fire on initial render, after saving the file, then it works.
if (attachedGifts !== null) {
let selectedIndex = -1
let filteredArray = data.map(val => {
if (val.id === attachedGifts.id) {
selectedIndex = walletData.indexOf(val);
setSelectedGiftIndex(selectedIndex);
return {
...val,
isSelect: val?.isSelect ? !val?.isSelect : true,
};
} else {
return { ...val, isSelect: false };
}
});
setData(filteredArray);
}
}, [attachedGifts]);
const attachGiftsToContext = (obj) => {
dispatch(SET_GIFTS(obj));
showToast('Gifts attached successfully!');
navigation?.goBack(); // goes back to GameScreen
}
return (
// somewhere in between
<TouchableOpacity onPress={attachGiftsToContext}>ATTACH</TouchableOpacity>
)
};
context:
import React, { createContext, useContext, useMemo, useReducer } from 'react';
const GiftsReducer = (state: Object | null, action) => {
switch (action.type) {
case 'SET_GIFTS':
return action.payload;
default:
return state;
}
};
const GiftContext = createContext({});
export const GiftsProvider = ({ children }) => {
const initialGiftState: Object | null = null;
const [attachedGifts, dispatch] = useReducer(
GiftsReducer,
initialGiftState,
);
const memoedValue = useMemo(
() => ({
attachedGifts,
dispatch,
}),
[attachedGifts],
);
return (
<GiftContext.Provider value={memoedValue}>
{children}
</GiftContext.Provider>
);
};
export default function () {
return useContext(GiftContext);
}
Output of console.log in GameScreen:
attached gifts: Object {
"reciptId": "baNlCz6KFVABxYNHAHasd213Fu1",
"walletId": "KQCqSqC3cowZ987663QJboZ",
}
What could possibly be the reason behind this and how do I solve this?
EDIT
Added related code here: https://snack.expo.dev/uKfDPpNDr
From the docs
When you call useEffect in your component, this is effectively queuing
or scheduling an effect to maybe run, after the render is done.
After rendering finishes, useEffect will check the list of dependency
values against the values from the last render, and will call your
effect function if any one of them has changed.
You might want to take a different approach to this.
There is not much info, but I can try to suggest to put it into render, so it might look like this
const filterAttachedGifts = useMemo(() => ...your function from useEffect... , [attachedGitfs])
Some where in render you use "data" variable to render attached gifts, instead, put filterAttachedGifts function there.
Or run this function in component body and then render the result.
const filteredAttachedGifts = filterAttachedGifts()
It would run on first render and also would change on each attachedGifts change.
If this approach doesn't seems like something that you expected, please, provide more code and details
UPDATED
I assume that the problem is that your wallet receive attachedGifts on first render, and after it, useEffect check if that value was changed, and it doesn't, so it wouldn't run a function.
You can try to move your function from useEffect into external function and use that function in 2 places, in useEffect and in wallet state as a default value
feel free to pick up a better name instead of "getUpdatedArray"
const getUpdatedArray = () => {
const updatedArray = [...walletData];
if (attachedGifts !== null) {
let selectedIndex = -1
updatedArray = updatedArray.map((val: IWalletListDT) => {
if (val?.walletId === attachedGifts?.walletIds) {
selectedIndex = walletData.indexOf(val);
setSelectedGiftIndex(selectedIndex);
setPurchaseDetailDialog(val);
return {
...val,
isSelect: val?.isSelect ? !val?.isSelect : true,
};
} else {
return { ...val, isSelect: false };
}
});
}
return updatedArray;
}
Then use it here
const [walletData, setWalletData] = useState(getUpdatedArray());
and in your useEffect
useEffect(() => {
setWalletData(getUpdatedArray());
}, [attachedGifts]);
That update should cover the data on first render. That might be not the best solution, but it might help you. Better solution require more code\time etc.
I am actually studying about enzyme integrated with jest to test stuff on applications.
Other things I am using is react and moment.
My question is probably very noob, but I just want to know why there is two argument stuff on this line:
wrapper.find('SingleDatePicker').prop('onFocusChange')({ focused });
is the 'formula' of this, this: objectExample.methodExample('argument1')('argument2'); ?
I will provide down below the whole code divided in two: the test code and the code tested.
Code tested:
export default class ExpenseForm extends React.Component {
constructor(props) {
super(props);
this.state = {
calendarFocused: false,
};
}
onFocusChange = ({ focused }) => {
this.setState(() => ({ calendarFocused: focused }));
};
render() {
return (
<div>
{this.state.error && <p>{this.state.error}</p>}
<form onSubmit={this.onSubmit}>
<SingleDatePicker
date={this.state.createdAt}
onDateChange={this.onDateChange}
focused={this.state.calendarFocused}
onFocusChange={this.onFocusChange}
numberOfMonths={1}
isOutsideRange={() => false}
/>
</form>
</div>
)
}
}
test code:
test('should set calendar focus on change', () => {
const focused = false;
const wrapper = shallow(<ExpenseForm />);
wrapper.find('SingleDatePicker').prop('onFocusChange')({ focused });
expect(wrapper.state('calendarFocused')).toBe(focused);
});
So basically, I can understand all the functionality of the code itself and everything.
I just do not understand this second argument ({ focused }).
I don't even know if this is called 'argument'.
I tweak a bit and took this thing out of the code and it worked the same way.
I am kind of confused if this is vanilla javascript or something of one of these libraries I am using.
...
What I expect:
answer for what is this thing and why use it like this.
some source of name or something I can browse about it and learn that.
wrapper.find('SingleDatePicker').prop('onFocusChange') returns a function.
This function is actually:
onFocusChange = ({ focused }) => {
this.setState(() => ({ calendarFocused: focused }));
};
i.e. the one that you created in your component.
Now to execute this function you can do:
wrapper.find('SingleDatePicker').prop('onFocusChange')({ focused });
or
const onFocusChangeFn = wrapper.find('SingleDatePicker').prop('onFocusChange');
onFocusChangeFn({ focused });
In general if you have something like:
const myObject = {
getFunction: function(y) {
return function(x) {
console.log(y, x);
}
}
}
Then you can do:
myObject.getFunction(10)(20) // prints (10,20)
where myObject.getfunction(10) will return the inner function that you can call with any argument like 20 in the example.
Returning a function is useful for various purposes like currying and partial functions
so i understood that I can call the inner function as you mentioned on the last example. and that this example:
const onFocusChangeFn = wrapper.find('SingleDatePicker').prop('onFocusChange');
onFocusChangeFn({ focused });
is as the jest documentation suggest
const mockFn = jest.fn();
mockFn();
expect(mockFn).toHaveBeenCalled();
and that is pretty much clear on my mind.
problem is that when i get a piece of code like this:
test('should handle date changes', () => {
const startDate = moment(0).add(2, 'days');
const endDate = moment(0).add(3, 'days');
wrapper.find('DateRangePicker').prop('onDatesChange')({ startDate, endDate });
expect(setStartDate).toHaveBeenLastCalledWith(startDate);
expect(setEndDate).toHaveBeenLastCalledWith(endDate);
});
would this proceed like this?:
(...)
const wrapperFn = wrapper.find('DateRangePicker').prop('onDatesChange');
wrapperFn({ startDate, endDate });
I was proceeding on the studies and i found another thing that is about the same subject but a bit different.
test('should sort by date', () => {
const value = 'date';
wrapper.find('select').simulate('change', { target: { value } });
expect(sortByDate).toHaveBeenCalled();
});
he defined the target value for the 'e' event inside the simulate() arguments. this could be defined separately too ? Like this:
test('should sort by date', () => {
const value = 'date';
const wrapperFn = wrapper.find('select').simulate('change')
wrapperFn({ target: { value } });
expect(sortByDate).toHaveBeenCalled();
});
or they are completely different things?
below, i will provide the full code I am testing
import React from 'react';
import { connect } from 'react-redux';
import { DateRangePicker } from 'react-dates';
import { setTextFilter, sortByDate, sortByAmount, setStartDate, setEndDate } from '../actions/filters';
export class ExpenseListFilters extends React.Component {
state = {
calendarFocused: null
};
onDatesChange = ({ startDate, endDate }) => {
this.props.setStartDate(startDate);
this.props.setEndDate(endDate);
};
onFocusChange = (calendarFocused) => {
this.setState(() => ({ calendarFocused }));
}
onTextChange = (e) => {
this.props.setTextFilter(e.target.value);
};
onSortChange = (e) => {
if (e.target.value === 'date') {
this.props.sortByDate();
} else if (e.target.value === 'amount') {
this.props.sortByAmount();
}
};
render() {
return (
<div>
<input
type="text"
value={this.props.filters.text}
onChange={this.onTextChange}
/>
<select
value={this.props.filters.sortBy}
onChange={this.onSortChange}
>
<option value="date">Date</option>
<option value="amount">Amount</option>
</select>
<DateRangePicker
startDate={this.props.filters.startDate}
endDate={this.props.filters.endDate}
onDatesChange={this.onDatesChange}
focusedInput={this.state.calendarFocused}
onFocusChange={this.onFocusChange}
showClearDates={true}
numberOfMonths={1}
isOutsideRange={() => false}
/>
</div>
);
}
};
const mapStateToProps = (state) => ({
filters: state.filters
});
const mapDispatchToProps = (dispatch) => ({
setTextFilter: (text) => dispatch(setTextFilter(text)),
sortByDate: () => dispatch(sortByDate()),
sortByAmount: () => dispatch(sortByAmount()),
setStartDate: (startDate) => dispatch(setStartDate(startDate)),
setEndDate: (endDate) => dispatch(setEndDate(endDate))
});
export default connect(mapStateToProps, mapDispatchToProps)(ExpenseListFilters);
fixtures:
import moment from 'moment';
const filters = {
text: '',
sortBy: 'date',
startDate: undefined,
endDate: undefined
};
const altFilters = {
text: 'bills',
sortBy: 'amount',
startDate: moment(0),
endDate: moment(0).add(3, 'days')
};
export { filters, altFilters };
thanks for helping.
everyone! I use react and material ui library. I want to handle click event and long-press event separately. I think problem related to async set state, but for now, I don't know how to handle this events
const [isCommandHandled, setIsCommandHandled] = React.useState(null);
const handleButtonPress = function (e) {
setIsCommandHandled(false);
console.log('ON_MOUSE_DOWN ' + isCommandHandled); // here value null
buttonPressTimer = setTimeout(handleLongPress, 1500, e);
}.bind(this);
const handleLongPress = (e) => {
if (!isCommandHandled) {
setIsCommandHandled(true);
console.log('TIMER_IS_EXECUTED' + isCommandHandled); //Here value false or null
// some other logic for long press event
}
clearTimeout(buttonPressTimer);
};
const handleButtonRelease = function (e) {
if (!isCommandHandled) {//isCommandHandled isn't updated here, as a result logic is executed always
// got regular click, not long press
// specific logic
setIsCommandHandled(true);
}
clearTimeout(buttonPressTimer);
};
<IconButton
onMouseDown={(e) => handleButtonPress(e)}
onMouseUp={(e) => handleButtonRelease(e)}
>
```
You can use setState with callback and put the set timeout ID to state:
setIsCommandHandled((prevState)=>{
console.log("TIMER_IS_EXECUTED" + isCommandHandled); //Here value false or null
return true; });
Working Example:
https://codesandbox.io/s/material-demo-gc0le
This is how I handle a long press:
//import Hooks
import { useState, useEffect } from "react";
const Component = () => {
//pressState
const [pressed, setPressed] = useState();
//handleLongPress
useEffect(() => {
const timer = pressed
? setTimeout(() => {
console.log(pressed, "got pressed!");
}, 1300)
: null;
return () => clearTimeout(timer);
}, [pressed]);
//pressedElement
return (
<div
onMouseDown={(e) => setPressed(e.target)}
onMouseUp={() => setPressed()}
style={{ backgroundColor: "lightgreen" }}
>
Press me
</div>
);
};
export default Component;
Tested here: https://codesandbox.io/s/bold-bose-7vx3qg
I'm trying to build a simple timer that will start and stop on click.
all my project is functional component based (using recompose), so I'm not sure where to set the setInterval.
here is a thing I tried to play with until I was completely lost where to store the setInterval so I'll be able to clear it on onStop fn (that will fire on a button) - as in functional component there is no this that I can put the timer and remove it from ... what's the functional components way of doing it ?
https://codepen.io/anon/pen/jQQZrm?editors=0010
any suggestions ?
- using react-native
thanks.
You need here 3 different state handlers: stopTimer, startTimer and updateValue(I've used slightly different naming than your code did).
In startTimer you need to create timer that runs updateValue by timer. In other words you need to call indirectly one state handler from another.
There is no way doing that. But. You can split those handlers into 2 sets: "value + updateValue" and "stopTimer + startTimer + intervalId". Then you will be able to get state handlers from first set in second as props:
const EnchanceApp = compose(
withStateHandlers({
timer: 0,
}, {
updateValue: ({timer}) =>
() => ({timer: timer + 1})
}),
withStateHandlers({
timerId: 0,
}, {
startTimer: ({timerId}, {updateValue}) =>
() => {
clearInterval(timerId);
return {
timerId: setInterval(updateValue, 1000)
};
},
stopTimer: ({timerId}) =>
() => clearInterval(timerId)
})
)(App);
works perfect, my code sample:
const BgList = ({ bgs }) => (
<PoseGroup>
{bgs.map(item => <StyledBg key={item} style={{backgroundImage: 'url(/img/'+item+'.jpg)'}} />)}
</PoseGroup>
);
const enhance = compose(
withStateHanlders(
() => ({
index: 0,
isVisible: false,
bgs: _.shuffle([0,1,2,3]),
timerId: 0,
}),
{
startTimer: () => ({timerId}, {updateValue}) => {
clearInterval(timerId);
return {
timerId: setInterval(updateValue, 5000)
};
},
stopTimer: ({timerId}) => () => clearInterval(timerId),
updateValue: ({bgs}) =>
() => {
return ({bgs: _.shuffle(bgs)})
},
},
),
lifecycle({
componentDidMount() {
const {timerId, updateValue} = this.props;
this.props.startTimer({timerId}, {updateValue})
}
}),
)
const BlockAnimated = enhance(({
bgs
}) => {
return (
<BgList bgs={bgs} />