React.js Getting State to Immediately Update - javascript

I'd like for my state to update changes to immediately be shown when state changes, but I can't seem to figure out why It isn't. Basically when a user clicks on a dropdown item from the menu, the items inner text ... that they clicked on should appear as an h1 on the screen, but instead it doesn't appear until the next click. How can I change this? Hopefully I made sense. Code can be found here.
Parent Component (APP):
class App extends React.Component {
state = {
loading: true,
bases: ['USD', 'EUR', 'AUD', 'CAD', 'JPY', 'NZD'],
selectedBase: null
};
// When Component Mounts Overlay goes for 3 Seconds
componentDidMount() {
setTimeout(() => this.setState({
loading: false,
}), 3000)
this.onBaseChange('USD');
}
// When User selects a new Base in Search Component, state is updated
onBaseChange = newBase => {
this.setState({ selectedBase: newBase });
}
// need to find out how to see state change immediatly after its updated!
// Rendered Content:
render(){
return (
<>
{this.state.loading === false ? (
<div className="App">
<div id="one">
<h1>{this.state.selectedBase}</h1>
<Search bases = {this.state.bases} selectedBase = {this.state.selectedBase} onBaseChange = {this.onBaseChange}/>
</div>
</div>
) : (
<Overlay />
)}
</>
);
}
}
export default App;
Child Component (Search):
class Search extends Component {
state = {
dropdownVisible: false,
term: '',
selectedBase: this.props.selectedBase
};
// when a base is clicked from dropdown, the selectedBase is updated, term is set back to empty, and dropdown back to non-visible.
// passing state of child up to parent through prop
// clearing input search on click
onBaseSelect = (event) => {
// when an base is clicked from dropdown, the selectedBase is updated, term is set back to empty, and dropdown back to nonvisible.
this.setState({
selectedBase: event.target.innerHTML,
term: '',
dropdownVisible: false
})
// passing state of child up to parent through prop
this.props.onBaseChange(this.state.selectedBase)
// clearing input search on click
document.getElementById("input_search").value = "";
}
render(){
return(
<div id="search">
<div id="dropdown" style={{display: this.state.dropdownVisible ? "block" : "none"}}>
<ul>
{/* filterng out base array based on users input */}
{this.props.bases.filter(base => base.includes(this.state.term.toUpperCase())).map((filteredBase, index) => (
<li onClick = {this.onBaseSelect} key={index}>{filteredBase}</li>
))}
</ul>
</div>
</div>
)
}
}
export default Search

this.setState is an asynchronous function, so when you do
// passing state of child up to parent through prop
this.props.onBaseChange(this.state.selectedBase)
// clearing input search on click
document.getElementById("input_search").value = "";
the state is not yet updated. So send that code as as a callback to this.setState like this,
onBaseSelect = (event) => {
// when an base is clicked from dropdown, the selectedBase is updated, term is set
back to empty, and dropdown back to nonvisible.
this.setState({
selectedBase: event.target.innerHTML,
term: '',
dropdownVisible: false
},
()=>{
// passing state of child up to parent through prop
this.props.onBaseChange(this.state.selectedBase)
// clearing input search on click
document.getElementById("input_search").value = "";
);
}

Related

open ant design popover from two link

I have two component: 1:StudentList 2: Major in react and antd.
StudentList Component rendered a list of students.
Major Component made a list of majors that you can pick them. After selecting major, the selected major title display on the top of the students list. and the list will be filtered according to the selected major.
This is StudentList component contain Major component:
class StudentList extends Component {
render(){
return(
<>
<Major/>
<h5>20 student found in <a>selected major</a></h5>
<List>
//this is the list of students and is not related to this question
</List>
</>);
}
}
This is Major Component with a filter button to open the popover:
class Major extends Component {
render() {
return (
<Popover
trigger="click"
content={content} //list of majors
>
<Button>
<FilterOutlined /> Select major to filter
</Button>
</Popover>
);
}
}
When I click on the Select major to filter button, the popover open to select majors. I want to change the code in order to open this popover from two place:
1- click on Select major to filter button in the Major component
2- click on selected major in the title in StudentList component.
Notice: I want to open the same popover in the same place (similar to when I click on Select major to filter button)
Maybe it could handle with state and handleVisibleChange function. but I don't know how to handle it from 2 components. I glad to hearing your solutions.
You can use the visible and onVisibleChange property from Antd's tooltip because they are used by the PopOver as well. You can find an easy example from Andt how to control a PopOver by visible in the docs.
To get the button click you can use onClick from antd's Button Api.
The desired example using React Components:
class Major extends Component {
componentDidUpdate(prevProps) {
// Typical usage (don't forget to compare props):
if (this.props.value !== prevProps.value) {
this.setState({ visible: this.props.value });
}
}
state = {
visible: false
};
hide = () => {
this.setState({
visible: false
});
};
handleVisibleChange = visible => {
this.setState({ visible });
// this.props.onChange(visible); // add me to open popover on every click on studenlist
};
render() {
return (
<Popover
trigger="click"
content={<a onClick={this.hide}>Close</a>}
visible={this.state.visible}
onVisibleChange={this.handleVisibleChange}
>
<Button>Select major to filter</Button>
</Popover>
);
}
}
class StudentList extends Component {
state = {
visible: false
};
onClick = () => {
this.setState({ visible: !this.state.visible });
};
render() {
return (
<>
{/* <Major value={this.state.visible} onChange={setVisible} /> */}
<Major value={this.state.visible} />
<h5>
20 student found in <a>selected major</a>
</h5>
<Button onClick={this.onClick}>Select major from Studenlist</Button>
</>
);
}
}
Component example as a CodeSandBox.
Here is a simple example for your request using react hooks and simple buttons to open the PopOver:
function Major({ value, onChange }) {
const [visible, setVisible] = useState(value);
useEffect(() => {
value && setVisible(value);
}, [value]);
const hide = () => setVisible(false);
const handleVisibleChange = visible => {
setVisible(visible);
onChange(visible);
};
return (
<Popover
trigger="click"
content={<a onClick={hide}>Close</a>}
visible={visible}
onVisibleChange={handleVisibleChange}
>
<Button>Select major to filter</Button>
</Popover>
);
}
function StudentList() {
const [visible, setVisible] = useState(false);
const onClick = () => {
setVisible(true);
};
return (
<>
<Major value={visible} onChange={setVisible} />
<h5>
20 student found in <a>selected major</a>
</h5>
<Button onClick={onClick}>Select major from Studenlist</Button>
</>
);
}
The depended working CodeSandBox.
Edit1: Added React Component example.

react.js how to avoid parent state update on child component cancel

In my react.js project I have parent and child components (Page like parent and Modal with input like child). I receive data in parent by ajax request and pass it for input from parent to child and fill the child state with it. Also in child component I have 2 buttons: Submit and Cancel. Conside the following code:
**Parent**
render() {
const { projectAux } = this.props;
return (
<ProjectNameFormModal
projectAux={projectAux}
onVisibleChange={e => this.onVisibleProjectNameFormModalChange(e)}
visible={this.state.editProjectNameModalVisible}
/>
)
}
**Child**
import React from 'react';
import {Button, Checkbox, Form, Input, List, Modal, Select} from "antd";
class ProjectNameFormModal extends React.Component{
constructor(props){
super(props);
this.state = {
projectAux: props.projectAux,
visible: props.visible
}
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
if(this.state.projectAux != nextProps.projectAux){
this.setState({
projectAux: nextProps.projectAux,
visible: nextProps.visible
});
}
return true;
}
handleProjectNameInputChange = e => {
let current_state = this.state;
current_state.projectAux[e.target.name] = e.target.value;
this.setState(current_state);
}
handleCancelSubmitProjectNameUpdate = e => {
this.props.onVisibleChange(false);
}
handleSubmitProjectNameUpdate = e => {
console.log(' in handleSubmitProjectNameUpdate');
this.setState({...this.state, visible: false});
this.props.onVisibleChange(false);
}
render() {
return (
<Modal
title='Edit project Name'
visible={this.props.visible}
bodyStyle={{}}//height:"800px"
onSave={{}}
maskClosable={false}
onCancel={this.handleCancelSubmitProjectNameUpdate}
footer={[
<Button key="back" onClick={this.handleCancelSubmitProjectNameUpdate}>
Cancel
</Button>,
<Button key="submit" type="primary" onClick={this.handleSubmitProjectNameUpdate}>
Save
</Button>,
]}
>
<div>
<Input placeholder="ProjectName"
name="name"
onChange={this.handleProjectNameInputChange}
value={this.state.projectAux && (this.state.projectAux.name)}
/>
</div>
</Modal>
);
}
}
export default ProjectNameFormModal;
So the problem is when I enter some new data to input and then press Cancel button, I have also my Parent state updated to the new data which I definetely dont want to happen. When Cancel is pressed, no updates to the parent state should occur but it happens. Btw, parent state does not update when I enter new symbols into input. I tried to use spread operator as it is said here but it did not work.
Any ideas how to fix it would be welcome, thank you.

Input state not binding onChange on first click

I have declared a state called account_type. I have created an onChange event which changes the value of the state upon clicking the div.
<div
className="price-plan"
value="star"
onClick={() => this.setPlan("star")}
>
The issue is that the account_type state does not get updated the first time I click on the div. It only gets updated when I click on it twice. Is there a way to update the state just by clicking the div. Here's an excerpt from my code showing what I am trying to do
let isRedirect = false;
class PricePlan extends React.Component {
constructor(props) {
super(props);
this.state = {
account_type: "",
renderRedirect: false
};
this.handleChange = this.handleChange.bind(this);
}
// Handle fields change
handleChange = input => e => {
this.setState({ [input]: e.target.value });
};
setPlan(plan) {
this.setState({
account_type: plan
});
console.log(this.state.account_type);
// if (this.state.account_type !== undefined) {
// isRedirect = true;
// }
}
render() {
if (isRedirect) {
return (
<Redirect
to={{
pathname: "/sign-up",
state: { step: 2, account_type: this.state.account_type }
}}
/>
);
}
return (
<div
className="price-plan"
value="star"
onClick={() => this.setPlan("star")}
>
<h3>{this.props.planName}</h3>
<div className="mute price-row">Name</div>
<p className="price">Price</p>
<span className="billed-frequency">Cycle</span>
</div>
);
}
}
As #Jayce444 suggests, setState do not immedeately updates state. So setPlan should look like
setPlan(plan) {
this.setState({
account_type: plan
});
console.log(plan); // Don't expect immediate state change in event handler
}
But you can use this.state.account_type anywhere in render() function. And rendering will happen after this.state.account_type is updated on first click.

Toggle state from custom state variables

I have a reactstrap Collapse component that I'm trying to toggle state from an external Button that sits within a loop of mapped items using custom state variables.
My question:
Why does it only open and not toggle the collapse component when I have the state on my openCollapse method to setState to !state.collapse?
My code:
// some_items.js (brief example)
// State
this.state = {
toggleCollapse: false
}
// my custom state variable that I want to have toggle
openCollapse(itemId) {
this.setState(state => ({
[`toggleCollapse-${itemId}`]: !state.collapse
}));
}
// mapped item with button trigger for toggling the collapse
<div key={item.id>
<Button
onClick={() => {
this.openCollapse(item.id);
}}
>
View Listed Item Info
</Button>
//
// Some extra content that belongs here in between..
//
<ItemInfoCollapse
show={this.state[`toggleCollapse-${item.id}`]}
item={item}
/>
</div>
// My item_collapse.js
class ItemInfoCollapse extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
collapse: false,
isOpen: this.props.show
};
}
componentWillReceiveProps(nextProps) {
this.setState({ isOpen: nextProps.show });
}
toggle = () => {
this.setState({ collapse: !this.state.collapse });
};
render() {
return (
<Collapse isOpen={this.state.isOpen} className={this.props.className}>
// Some JSX markup
</Collapse>
)
}
What dictates whether your Collapse component gets open or closed is based on the show prop that you are passing into it from your parent component.
It appears you have everything set up correctly, with the exception of your state variable that you're using in your openToggle function - !state.collapse. I don't see the collapse variable anywhere which means it's undefined so it's actually running !undefined which always evaluates to true (you can test this in a browser console).
I think what you mean is !state[toggleCollapse-${itemId}] instead of !state.collapse

Passing value from Parent to Child in Reactjs using props

I am trying to pass theExpert.email value to another child component whenever i click on send questions button.
This is the constructor of the Parent class
constructor() {
super();
this.state = {
textbox: '',
category: '',
exp: '',
hide: false
};
this.hideButton = this.hideButton.bind(this);
}
The follow function hides the button once it is clicked and saves the value of the theExpert.email in exp, (value is passed correctly since console.log prints it)
hideButton (value) {
this.setState({ hide: true });
this.setState({
exp: value
});
console.log(value)
}
And here is the button that once I click on it it passes the value to hideButton
<div>
{!this.state.hide ? (
<button onClick={() => this.hideButton(theExpert.email)}>Ask Me!</button>
) : null}
</div>
Now what i want to do is once i click on Send Questions button i get redirected to the child component Questions.js and pass the value of theExpert.email to that component
The button Send Questions:
<div>
<p>
{(this.state.hide) && (
<Link to="/questions">
<button
style={{ background: "green", color: "white" }}
>
Send Question
</button>
</Link>
)}
</p>
</div>
How can i retrieve value in the child class to create a post request using it
I think the issue us due to react being lazy. Basically you are just setting hide to true and re-rendering your component and all of its children but never setting exp the expected value.
So instead of
hideButton (value) {
this.setState({ hide: true });
this.setState({
exp: value
});
console.log(value)
}
try
hideButton (value) {
this.setState(prevState=>{
prevState= {
...prevState
hide: true ,
exp: value
}
return prevState
});
}

Categories