React Hooks Onchange Synchronize - javascript

Im passing my app from the class model to the hooks and in my code y have a dropdown list that when executes the onChange method it does this:
filter(event)
{
this.setState({
selectedFilter: event.target.value
},() => { this.chargeInfo(); });
}
In my class model works perfectly, but i dont know how to do this with the hooks, im passing the method chargeInfo() and the variable selectedFilter by the props because this component is a child, so what I have is this code:
<Dropdown onChange={onChangeDropdown} />
const onChangeDropdown = function(event)
{
props.changeSelectedFilter(event.target.value);//this do setSelecedFilter in the parent
props.chargeInfo();//this is the method of the parent that do something
}
How can I adapt this in a Sync mode that execute first the props.changeSelectedFilter and later the props.chargeInfo?.

You may need to use the State Hook like this:
// setting up the default dropdown value
const [dropdownValue, setDropdownValue] = useState('Some Value');
// later, setting a new dropdown value
setDropdownValue('New Value')
See https://reactjs.org/docs/hooks-state.html for more info

I solved using useEffect:
useEffect(
() => {
// eslint-disable-next-line react-hooks/exhaustive-deps
props.chargeInfo();}, [props.selectedFilter]
)
const onChangeDropdown = function(event)
{
props.changeSelectedFilter(event.target.value);
}
return(
<Dropdown style={{ marginRight: 10, }} value={props.selectedFilter} options={filters}
onChange={onChangeDropdown} optionLabel="name" placeholder="Filter by" />
)

Related

In React when in onChange function how to update another custom component's state and pass a value in?

In a React app currently there is a drop down list that has an onChange event that calls a function. In that function (when the users selects a different choice in the ddl) what I would like to achive is to update another custom component & pass a value into that component.
So in the front end there is a simple drop down:
<Dropdown
value={selectedOption}
options={dropDownOptions}
onChange={onChange}
/>
Then there is an onChange function that gets fired when the drop down gets selected:
const onChange = React.useCallback(
e => {
const optionId = e.target.value;
const optionData = keyedOptions[optionId];
// refresh DownloadSelector custom component
// something like this which doesn't work {optionData.id && <DownloadSelector eventId={optionData.id} />} }
Also I am able to import the custom component at the top of the file normally such as:
import { DownloadSelector } from '../../../SearchAndSort/DownloadSelector';
The custom component when defining it has a value passed in like so:
export const DownloadSelector = ({eventId}) => {
If the whole page refreshes the custom DownloadSelector component will get uploaded. I would like that to happen in the onChange.
How in the onChange function can we update/reload/setState/refresh the DownloadSelector component?
const [eventId, setEventId] = React.useState()
const onChange = React.useCallback(() {
// ....
// replace newState with a updated value
setEventId( newState )
}, [])
return <DownloadSelector eventId={eventId} />
In DownloadSelector do something like this:
function useForceUpdate(){
const [value, setValue] = useState(0); // integer state
return () => setValue(value => value + 1); // update state to force render
}
const DownloadSelector = ({eventId}) => {
const forceUpdate = useForceUpdate();
// ....
return <Dropdown
value={selectedOption}
options={dropDownOptions}
onChange={onChange}
forceUpdate={forceUpdate}
/>
}
And then in onChange in Dropdown Component:
const onChange = React.useCallback(() {
// ....
forceUpdate()
}, [])
The answer for my question is in the custom component that I wanted to rerender when the drop down ( a different component) is changed is to define the variables that are changing in the custom component DownloadSelector in state (not a local let) such as:
const [isMyVar] = useState(false);
Then when isMyVar is updated the state changed & the component rerenders. The answer was not (what I originally thought) was to have the code in the onChange event of the 2nd component. I appreciate #Hostek for your help.

React functional component test file unable to update state value and call its props function

i do have a functional parent component and it do have a state value , which have default value as false and it is calling another component which is visible only when the state value changes to true and the child component do have 2 function in it.
Illustrating with code
export const MainSearch = (props) => {
const [search, setSearch] = useState(false);
const closeSearch = () => {
setSearch(false);
ANALYTICS.trackEvent('popup_collpsed');
}
const toggleSearch = async () => {
await setSearch(true);
ANALYTICS.trackEvent('popup_expanded');
}
};
return (
<React.Fragment>
<searchBar toggleSearch={toggleSearch} />
{search &&
<childSearch
toggleSearch={toggleSearch}
closeSearch={closeSearch}
/>}
</React.Fragment>
)
}
And its test file with one test case
describe('MainSearch',()=>{
it('AdvancedSearch - Toggle popup_expanded and popup_collapsed ', async () => {
const component = shallow(<MainSearch {...props} />);
const searchBar = component.find('searchBar');
await searchBar.props().toggleSearch(); // calling function
expect(ANALYTICS.trackEvent).toHaveBeenCalledWith('popup_expanded');
const childSearchComponent = component.find('childSearch'); // not working ,since state value hides
expect(childSearchComponent).toBeDefined();
await advancedSearchComponent.props().closeSearch();// here getting null for .props()
expect(ANALYTICS.page.trackEvent).toHaveBeenCalledWith('popup_collapsed');
});
});
i know its possible with component.update for CLASS COMPONENTS, but here am using functional components and am getting error
if i remove the state value search , am getting my test case PASS, but cant remove that , its needed. so my test case need to make the state value true and call the function closeSearch and then check the analytics.
BUT am getting error Method “props” is meant to be run on 1 node. 0 found instead.
I guess state value if false and its not getting that particular node .
Can you guys please help me on same , since am stuck with it and can give more info if needed
Take a look at your toggleSearch function. You're awaiting setSearch, which isn't a promise. Remove the await Keyword and you should be fine!
If you would want to trigger your Analytics call only after the State has been set, you would need to hook in to Reacts useEffect Hook.
Then you could do something like this:
useEffect(() => {
if(search){
ANALYTICS.trackEvent('popup_expanded');
}
}, [search])
https://reactjs.org/docs/hooks-effect.html

cannot update a component from inside the function body of a different component - React Native?

I have a child component "Text Input" and passes the value to as a prop like this
export default function MobileInput(props) {
const [mobileNumber, setMobileNumber] = React.useState('');
return (
<View style={styles.inputBox}>
<TextInput
value={mobileNumber}
onChangeText={(number) => setMobileNumber(number)}
onEndEditing={props.saveMobileNumber(mobileNumber)} // here
/>
</View>
);
}
In Parent, I got the value from child
const [mobile, setMobile] = useState('');
const getMobile = (number) => {
number ? setMobile(number) : null; // here's I got this warnning
console.log('getMobile-number-from-child', number);
};
const reSendMobile = () => { // other function I want to call passed on mobile number I got from child component
if (mobile?.length) {
alert('Done');
setReSend(false);
setSeconds(10);
} else {
alert('Please write your number before press send!');
}
};
<MobileInput saveMobileNumber={getMobile} />
I see this issue But I'm already using React 16.13.1
TextInputs property onEndEditing accepts a function that is called when text input ends.. Instead of a function, you are passing the result of your props.saveMobileNumber function that is called when the component renders. Try passing a function that calls saveMobileNumber instead:
onEndEditing={() => props.saveMobileNumber(mobileNumber)}
Your code will be much easier to read/debug if you avoid keeping the same state in multiple components. You can pass mobile and setMobile to the child through props and avoid having to create separate state for the same data.
Try this:
<View style={styles.inputBox}>
<TextInput
value={mobileNumber}
onChangeText={(number) => setMobileNumber(number)}
onEndEditing={() => props.saveMobileNumber(mobileNumber)} // change here
/>
</View>
The event onEndEditing accepts a function call
Just update to call a arrow function :
onEndEditing={() => props.saveMobileNumber(mobileNumber)}
For me, i was updating activity title outside the useEffect hook. When i moved the code
into useEffect hook, the error just gone.

Use dynamically created react components and fill with state values

Below is a proof of concept pen. I'm trying to show a lot of input fields and try to collect their inputs when they change in one big object. As you can see, the input's won't change their value, which is what I expect, since they're created once with the useEffect() and filled that in that instance.
I think that the only way to solve this is to use React.cloneElement when values change and inject the new value into a cloned element. This is why I created 2000 elements in this pen, it would be a major performance hog because every element is rerendered when the state changes. I tried to use React.memo to only make the inputs with the changed value rerender, but I think cloneElement simply rerenders it anyways, which sounds like it should since it's cloned.
How can I achieve a performant update for a single field in this setup?
https://codepen.io/10uur/pen/LYPrZdg
Edit: a working pen with the cloneElement solution that I mentioned before, the noticeable performance problems and that all inputs rerender.
https://codepen.io/10uur/pen/OJLEJqM
Here is one way to achieve the desired behavior :
https://codesandbox.io/s/elastic-glade-73ivx
Some tips :
I would not recommend putting React elements in the state, prefer putting plain data (array, objects, ...) in the state that will be mapped to React elements in the return/render method.
Don't forget to use a key prop when rendering an array of elements
Use React.memo to avoid re-rendering components when the props are the same
Use React.useCallback to memoize callback (this will help when using React.memo on children)
Use the functional form of the state setter to access the old state and update it (this also helps when using React.useCallback and avoid recreating the callback when the state change)
Here is the complete code :
import React, { useEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const INPUTS_COUNT = 2000;
const getInitialState = () => {
const state = [];
for (var i = 0; i < INPUTS_COUNT; i++) {
// Only put plain data in the state
state.push({
value: Math.random(),
id: "valueContainer" + i
});
}
return state;
};
const Root = () => {
const [state, setState] = React.useState([]);
useEffect(() => {
setState(getInitialState());
}, []);
// Use React.useCallback to memoize the onChangeValue callback, notice the empty array as second parameter
const onChangeValue = React.useCallback((id, value) => {
// Use the functional form of the state setter, to update the old state
// if we don't use the functional form, we will be forced to put [state] in the second parameter of React.useCallback
// in that case React.useCallback will not be very useful, because it will recreate the callback whenever the state changes
setState(state => {
return state.map(item => {
if (item.id === id) {
return { ...item, value };
}
return item;
});
});
}, []);
return (
<>
{state.map(({ id, value }) => {
// Use a key for performance boost
return (
<ValueContainer
id={id}
key={id}
onChangeValue={onChangeValue}
value={value}
/>
);
})}
</>
);
};
// Use React.memo to avoid re-rendering the component when the props are the same
const ValueContainer = React.memo(({ id, onChangeValue, value }) => {
const onChange = e => {
onChangeValue(id, e.target.value);
};
return (
<>
<br />
Rerendered: {Math.random()}
<br />
<input type="text" value={value} onChange={onChange} />
<br />
</>
);
});
ReactDOM.render(<Root />, document.getElementById("root"));

How do I fix HandleToggleState?

I have a simple material-ui toggle in my react component. I want to use it to toggle state (false/true). If I start with useState(false) the first time I click the toggle it says false rather than true.
I'm wondering if another react hook would solve for this. useEffect, useCallback...
const Component = () => {
const [toggleValue, setToggleValue] = useState(false);
const handleToggleChange = () => {
setToggleValue(!toggleValue);
console.log("toggle value in handle: " + toggleValue);
};
return(
<FormControlLabel
control={
<Switch
checked={toggleValue}
onChange={handleToggleChange}
value="my toggle"
/>
}
/>
);
};
I would expect setPreseason(!preseason); to set the state opposite of what it currently is. True to false and false to true.
It probably is but when I log the state on the next line it logs the initial state and will always log the opposite of what the toggle is.
The state updater function returned by useState is asynchronous
If you need to react to state changes useEffect is the place for it
const Component = () => {
const [toggleValue, setToggleValue] = useState(false);
const handleToggleValue = () => {
setToggleValue(!toggleValue);
};
useEffect(() => {
console.log("toggleValue: " + toggleValue);
// second argument to useEffect is an array of dependencies
// this function is going to run every time one of the dependencies
// changes
}, [toggleValue])
return (
<FormControlLabel
control={
<Switch
checked={toggleValue}
onChange={handleToggleValue}
value="my toggle"
/>
}
/>
);
}
The issue is about which value toggleValue is inside the closure. Is not what you expect. Instead pass a callback to setToggleValue. You will get the current state back, that you can then change.
const handleToggleValue = () => {
setToggleValue((toggleValue) => !toggleValue);
}
You are doing it correctly, except that toggleValue is just a local variable and is not changed just by calling setToggleValue (the state will be changed, but that happens asynchronously).
You could do something like this instead:
const handleToggleValue = () => {
const newToggleValue = !toggleValue;
setToggleValue(newToggleValue);
console.log('toggleValue: ' + newToggleValue);
}
It depends what your intention is with the new toggle value. This simply fixes what you were doing with the console.log call. But you may run into further trouble after that, given that you are using the new toggle value before the state is updated. But that is for another SO question...

Categories

Resources