React js variable in render stays undefined - javascript

I have two functions as follows :
handleChangeInputStockName = async event => {
const stockName = event.target.value
this.setState({ stockName })
}
handleChangeInputTotalQuantity = async event => {
const totalQuantity = event.target.value
this.setState({ totalQuantity })
}
I have two components that control the above two functions
<InputText
type="text"
value={ totalQuantity }
onChange={this.handleChangeInputTotalQuantity}
/>
and
<InputText
type="text"
value={ stockName }
onChange={this.handleChangeInputStockName}
/>
In my render function both stockName and totalQuantity are always undefined, even though I can see changes being made to them in the InputText via the above two functions. I am able to see a state change however it goes back to undefined immediately. The render function is as follows :
render() {
var { stockName, totalQuantity, show, close, children } = this.state
console.log('stockName ====> ', stockName) //undefined
console.log('totalQuantity ====> ', totalQuantity) //also undefined
...
What am I doing wrong? Please help me out here. Thank you.
Here's a link to the sandbox for the Modal to which this code belongs.
https://codesandbox.io/s/divine-bush-rquid

Related

React put query string into form's value attribute

I have a search page, something/search?q=foo, whenever im at this page I want to put foo in my form's value tag. (Not for search purposes, I have a fully functioning search bar, I just want to show the client the last thing he searched for).
I've gotten the search term with: (window.location.href.indexOf("?q") != -1) ? window.location.href.substring(window.location.href.indexOf("?q") + 3) : '', this works, although when putting it into the forms value tag, react blocks immediately, it doesn't let me write anything into the input field. I think this is because it updates to this string super fast and you don't see it happening.
How would I achieve this, to update my form's value one time and thats it?
Here is my simplified code:
<input type="search" name="q" id="q" value={(window.location.href.indexOf("?q") != -1) ? window.location.href.substring(window.location.href.indexOf("?q") + 3) : ''} <--- This is where I want to put the search string
What i've tried so far is this:
this.state = {
value:''
}
...
handleTitle = (s) => {
this.setState({value:s})
}
...
<input ... value={this.state.value} onChange={this.HandleTitle((window.location.href.indexOf("?q") != -1) ? window.location.href.substring(window.location.href.indexOf("?q") + 3) : '')}
this results in infinite state updates
I would suggest you get the value of the search-param when the component mounts, and store it in the component's local state. Then read/update the value from state. Something like:
const [search, setSearch] = useState("");
useEffect(() => {
setSearch(new URLSearchParams(new URL(window.location.href).search).get('q'));
}, []);
return (
<intput type="text" value={search} onChange={e => setSearch(e.target.value)} />
);
I've not tested it, but you get the gist of it.
Anyway if you want to access the q natively.
Working example
https://8zwht.csb.app/?q=test
import React from "react";
import "./styles.css";
class App extends React.Component {
state = {
value: ""
};
componentDidMount() {
const search = new URL(window.location).searchParams;
const term = search.get("q");
if (term)
this.setState({
value: term
});
}
handleChange = (e) => {
this.setState({
value: e.target.value
});
};
render() {
return (
<input
type="text"
onChange={this.handleChange}
value={this.state.value}
/>
);
}
}
export default App;
It would be easier to provide a more concrete answer if you share the code or gist

Pass params from state to a checkbox tree

I have this code :
constructor(props) {
super(props);
this.dispatch = props.dispatch;
this.state = {
checked: [],
expanded: [],
};
const {dispatch, match, t} = this.props;
this.onCheck = this.onCheck.bind(this);
this.onExpand = this.onExpand.bind(this);
}
onCheck(checked) {
this.setState({ checked });
console.log(this.state.checked);
this.loadProducts(this.state.checked);
}
onExpand(expanded) {
this.setState({ expanded });
}
render() {
const { checked, expanded } = this.state;
console.log(checked);
........
<CheckboxTree
nodes={this.props.chosenCategory.children}
checked={checked}
expanded={expanded}
onCheck={this.onCheck}
onExpand={this.onExpand}
.......
/>
The problem is that in console.log from render function I have the correct checked value. But in the function onCheck checked have the previous value. I don't understand what's the problem. Can you help me please ?
Due to the asynchronous behaviour of setState, we can't guarantee that the following line will have the updated value, at that particular instance.
In your case, if you want to log the updated value inside onCheck, the you can use the callback provided by the setState function.
onCheck(checked) {
this.setState({ checked }, () => {
this.loadProducts(this.state.checked); //param - checked will also do
console.log(this.state.checked);
});
}
now the console.log will execute, after successful setState.
There are 2 types of setState, the first one takes an object as a parameter, and the second one takes a function as a parameter. The difference between the two is the first one is asynchronous and the second is synchronous.
So you need to use setState with function as a parameter.
onCheck(checked) {
this.setState((state) => ({ checked}));
console.log(this.state.checked);
this.loadProducts(this.state.checked);
}

Generic function in react with multiple state variables

How to write a generic function with various state variable change based on the dropdown.
for ex:
I have dropdown 1 and dropdown 2. If I change the
dropdown 1, I need to change the few state variables like a, b, c
dropdown 2, I need to change the few state variables like x, y, z
I can do this with 2 functions. But how to write a a generic function for this?
handleChange: function(e) {
//api call to xyz.com/getcompany/<company id> to get the job list here
this.setState({
selectedCompany: e.target.value,
companychange: "company changed. New value: " + e.target.value
})
},
handleChange2: function (e) {
// api call to xyz.com/jobstatus/<job id> to get the job status\(like how many position available for this job\) here
this.setState({
jobchange:"job changed. New value " + e.target.value
})
}
Codepen: https://codepen.io/asdhanapal/pen/WmwJPj?editors=0011
You could use a curried function to simplify the code a bit:
changeToState(fn) {
return e => this.setState(fn(e));
}
render() {
//...
<input onChange={changeToState(e => ({ selectedCompany: e.target.value, /*...*/ }))} />
<input onChange={changeToState(e => ({ jobChange: e.target.value, /*...*/ }))} />
}
If that is still to much boilerplate, you could extract the handleChange event into a functional component:
const StateChanger = ({ action, parent, ...props }) => (
<input onChange={e => parent.setState(action(e))} {...props} />
);
// inside a component's render:
<StateChanger parent={this} action={e => ({ selectedCompany: e.target.value })} style={{ color: red }} />
but as I already mentioned in the comments, that might remove repeated code a bit, but it doesn't improve readability / maintainability whatsoever.
You can use below snippet:
handleChange = event => {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
};
Reference to react docs section:
https://reactjs.org/docs/forms.html#handling-multiple-inputs
You can use following:
handleChange = event => {
const { name, value } = event.target;
this.setState({
[name]:value
})
}
// in input
<input name="username" onChange={this.handleChange} />
Try this:
handleChange: function(source, e) {
switch(source) {
case 'company':
//Need to do api call to get the job list here
this.setState({
selectedCompany: e.target.value,
companychange: "company changed. New value: " + e.target.value
})
break;
case 'job':
// Need to do api call to get the job status\(like how many position available for this job\) here
this.setState({
jobchange:"job changed. New value " + e.target.value
})
break;
};
},
<select value={this.state.selectedCompany} onChange={this.handleChange.bind(this, 'company')}>
<select value={this.state.selectedCompany} onChange={this.handleChange.bind(this, 'job')}>
As I read your description of the requirements, there are no functional dependencies between dropdown 1 and dropdown 2, so I'd split this up in two separate components.

React setState not immediately updating component view

I have a input field that is filtering the elements on of an array.
The search results are always one keystroke behind, I assume because setState doesn't instantly update the view? What's the best way to work around that?
class App extends Component {
constructor() {
super();
this.state = {
images:[],
searchfield: '',
filteredImages:[],
suggestedKeywords:[],
inputValue: ''
}
}
onSearchChange = (event) => {
this.setState({searchfield: event.target.value});
this.setState({inputValue: event.target.value});
let filteredImages = this.state.images.filter(image => {
return image.labels.includes(this.state.searchfield.toLowerCase());
});
console.log(event.target.value);
this.setState({filteredImages});
}
}
const SearchBox = ({searchfield, searchChange, inputValue}) => {
return (
<div>
<input
type="search"
value={inputValue}
onChange={searchChange}
placeholder="Search images..."
/>
</div>
);
}
The search results are always one keystroke behind, I assume because setState doesn't instantly update the view? What's the best way to work around that?
That isn't the problem.
Your problem is that you are assuming updates to setState occur instantly.
this.setState({searchfield: event.target.value}); //You update searchfield here
return image.labels.includes(this.state.searchfield.toLowerCase());
//but this.state.searchfield doesn't reflect the update yet!
So instead, simply work off of the updated value rather than the one from the store.
return image.labels.includes(event.target.value.toLowerCase());
setState is asynchronous, so you will be filtering by the old searchfield when you do this.state.searchfield.toLowerCase(). With this in mind you could do something like this instead:
onSearchChange = (event) => {
const { value } = event.target;
const newState = {
searchField: value,
inputValue: value
};
newState.filteredImages = this.state.images.filter(image => {
return image.labels.includes(value.toLowerCase());
});
this.setState(newState);
}

Can I set the state and call a function on one button click?

I have a button and when I click on it I'm using onClick to set the state and call a function. When I pair the two actions together, it seems to only set the state.
If I remove the sitting the state function, the handleClick function works. Can you have multiple function calls in an onClick function? I searched around here and found a post stating that you could, not sure if my code is incorrect.
class OverviewRender extends Component {
constructor(props) {
super(props);
this.state = {
Selected: 'MainMenu',
name: ''
}
}
componentDidUpdate() {
console.log("OverviewRENDER update name: " + this.state.name);
}
handleClick(e) {
e.preventDefault();
let helpFileName = this.state.name;
helpFileName = helpFileName.toLowerCase().trim();
//Cap the first letter in the name and add the rest of the name
helpFileName = helpFileName.charAt(0).toUpperCase() + helpFileName.substr(1);
this.props.handleHelpChange(helpFileName);
console.log("pressed " + helpFileName);
}
The button calling onClick...
<RaisedButton
label="Load Default Values"
name="One"
value={this.state.name}
onClick={() => {this.handleClick.bind(this), this.setState({ name: 'One' })}}
style={buttonStyle}
/>
That's because you're not executing the function. Function#bind returns a new function with the specified this and other supplied arguments bound. You have to execute it. Anyways, since you're using an arrow function, there's no need to bind anyway. Lastly, just set state inside handleClick, and pass in any variables you need to:
onClick={(e) => this.handleClick(e, 'One')}
And handleClick:
handleClick(e, num) {
this.setState({
name: num
}, () => {
e.preventDefault();
let helpFileName = this.state.name; //or num
helpFileName = helpFileName.toLowerCase().trim();
//Cap the first letter in the name and add the rest of the name
helpFileName = helpFileName.charAt(0).toUpperCase() + helpFileName.substr(1);
this.props.handleHelpChange(helpFileName);
console.log("pressed " + helpFileName);
});
}
Also note that I'm setting state first, then using a callback because setState is asynchronous. You have to wait until it is set to access it and get the right value.
You could either have the handleClick set the state, as mentioned in the previous answer, or your onClick could call handleClick in the callback:
<RaisedButton
label="Load Default Values"
name="One"
value={this.state.name}
onClick={() => { this.setState({ name: 'One' }, this.handleClick.bind(this) }}
style={buttonStyle}
/>

Categories

Resources