I have a simple modal where I writte some data and submit to database. I'm trying to implement a simple loading on the button, so the user receives feedback. Unfortunately, this isn't working.
constructor(props) {
super(props);
this.state = {
isLoading: false,
show: false,
list: []
}
}
onSubmit = async event => {
ref.onSnapshot(async doc => {
if (doc.data().status === 'accepted') {
const list = await this.get(name, age, id);
this.setState(prevState => ({
isLoading: !prevState.isLoading, // this doesn't work
list: list,
show: false // state for a modal
}, () => console.log('loading on submit', this.state.isLoading)))
}
}
}
<button
onClick={this.onSubmit}
disabled={this.state.isLoading ? true : false}>
{this.state.isLoading ? 'Loading...' : 'OK'}
</button>
Thank you! :)
You want to set isLoading to true before you get your data, and then set isLoading to false when the asynchronous request is complete.
Example
function getList() {
return new Promise(function(resolve) {
setTimeout(() => resolve([1, 2, 3]), 1000);
});
}
class App extends React.Component {
state = {
isLoading: false,
show: false,
list: []
};
onSubmit = event => {
this.setState({ isLoading: true });
getList().then(list => {
this.setState({
isLoading: false,
list,
show: false
});
});
};
render() {
return (
<button
onClick={this.onSubmit}
disabled={this.state.isLoading}
>
{this.state.isLoading ? "Loading..." : "OK"}
</button>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
trigger loading true before await call like below code.
onSubmit = async event => {
ref.onSnapshot(async doc => {
if (doc.data().status === 'accepted') {
this.setState({
isLoading : true
})
const list = await this.get(name, age, id);
this.setState(prevState => ({
isLoading: false,
list: list,
show: false // state for a modal
}, () => console.log('loading on submit', this.state.isLoading)))
}
}
}
Related
When i open a particular page, fetching is processing successfully and data received, however for some kind of reason, once page is refreshed the fetch is not even pending its not even visible in inspect. If something seems not right kindly point it out because I already spent ages trying to figure out why this is happening.
state = {
metaInfoDocs: [],
docs: [],
loading: false,
};
componentDidMount() {
this.props.selectedDocsStore.clear();
this.props.selectedDocsStore.setViewDocId(0);
this.setState({ loading: true });
this.props
.fetchMetaDocs()
.then((r) => this.setState({ metaInfoDocs: r.data, loading: false }))
.catch((err) => {
this.setState({ loading: false });
errorWithMessage("Could not load documents");
});
this.props.eventManager.on("viewDoc", (doc) => {
this.loadDocuments(doc.id);
});
}
export default class CentralDocuments extends React.Component {
render() {
return (
<GeneralDocPresenter
type={"centralInformationDocuments"}
fetchMetaDocs={() => getMetaInfoByType("central-document")}
loadDocument={(id) => getCentralDocuments(id)}
eventManager={new EventManager()}
childComponents={{
metaDocViewer: MetaInfoDocViewer,
metaView: MetaInfoListView,
}}
/>
);
}
}
You need to use componentDidUpdate() lifecycle method as well. For example:
updateDoc = () => {
this.props.selectedDocsStore.clear();
this.props.selectedDocsStore.setViewDocId(0);
this.setState({ loading: true });
this.props
.fetchMetaDocs()
.then((r) => this.setState({ metaInfoDocs: r.data, loading: false }))
.catch((err) => {
this.setState({ loading: false });
errorWithMessage("Could not load documents");
});
this.props.eventManager.on("viewDoc", (doc) => {
this.loadDocuments(doc.id);
});
}
componentDidUpdate() {
this.updateDoc()
}
componentDidMount() {
this.updateDoc()
}
You are only using componentDidMount(), you need to also put the code into componentDidUpdate().
Something like this ..
state = {
metaInfoDocs: [],
docs: [],
loading: false,
};
componentDidMount() {
this.props.selectedDocsStore.clear();
this.props.selectedDocsStore.setViewDocId(0);
this.setState({ loading: true });
this.props
.fetchMetaDocs()
.then((r) => this.setState({ metaInfoDocs: r.data, loading: false }))
.catch((err) => {
this.setState({ loading: false });
errorWithMessage("Could not load documents");
});
this.props.eventManager.on("viewDoc", (doc) => {
this.loadDocuments(doc.id);
});
}
componentDidUpdate() {
this.props.selectedDocsStore.clear();
this.props.selectedDocsStore.setViewDocId(0);
this.setState({ loading: true });
this.props
.fetchMetaDocs()
.then((r) => this.setState({ metaInfoDocs: r.data, loading: false }))
.catch((err) => {
this.setState({ loading: false });
errorWithMessage("Could not load documents");
});
this.props.eventManager.on("viewDoc", (doc) => {
this.loadDocuments(doc.id);
});
}
export default class CentralDocuments extends React.Component {
render() {
return (
<GeneralDocPresenter
type={"centralInformationDocuments"}
fetchMetaDocs={() => getMetaInfoByType("central-document")}
loadDocument={(id) => getCentralDocuments(id)}
eventManager={new EventManager()}
childComponents={{
metaDocViewer: MetaInfoDocViewer,
metaView: MetaInfoListView,
}}
/>
);
}
}
fixing the mistake
I new in ReactJS and i have one few question. I defined function showModal and but console.log() and
this.setState({show:!this.state.show});.
And after that I applied
this function onClick event for div element inside map function.
1st question: When I click on div element showModal work but in console I don't see my console.log.
2nd question: I want to make when you click on one div element it must add/show few new div elements but only for one div element (on which I clicked). But now when I click on one div element it add/show new elements for all div elements which had this showModal function.
How can i fix this
import React, { Component } from "react";
import Modal from '../components/modal/form'
const DEFAULT_QUERY = 'redux';
const PATH_BASE = 'URL which work correct';
class Actions extends React.PureComponent{
constructor(){
super();
this.state = {
result:null,
show:false,
helpId:null
};
this.setSearchTopStories = this.setSearchTopStories.bind(this);
this.showModal = this.showModal.bind(this);
this.handleClickFromParent = this.handleClickFromParent.bind(this);
this.onClose = this.onClose.bind(this);
}
onClose = e => {
this.setState({ show: false});
}
handleClickFromParent = e => {
this.setState({show: !this.state.show});
}
showModal = e => {
console.log('BABE');
this.setState({show: !this.state.show})
};
setSearchTopStories(result) {
this.setState({ result });
};
componentDidMount() {
fetch(`${PATH_BASE}`)
.then(response => response.json())
.then(result => this.setSearchTopStories(result))
.catch(error => error);
};
render(){
const { searchTerm, result } = this.state;
console.log('* Actions Pure*');
console.log(result);
console.log('=');
return(
<div>
{
(result !== null) ?
result.map(
(item,index) =>
<div>
<div onClick={()=>this.showModal()}>{item.name}</div>
<Modal
id = {index}
handleClickFromParent {this.handleClickFromParent}
item = {[item]}
show = {this.state.show}
onClose = {this.onClose}>
YOLO
</Modal>
</div>
)
: null
}
</div>
)
}
}
export default Actions;
While selecting u can pass the item on method, and on click u can set the item value. Please check the below code.
Demo:
https://codesandbox.io/s/stackoverflowmodal-19i36
this.state = {
result: null,
show: false,
selectedItem:null,
helpId: null
};
//
showModal = (selectedItem) => {
this.setState({
show: !this.state.show,
selectedItem
});
};
//
class Actions extends React.PureComponent {
constructor() {
super();
this.state = {
result: null,
show: false,
selectedItem: null,
helpId: null
};
this.setSearchTopStories = this.setSearchTopStories.bind(this);
this.showModal = this.showModal.bind(this);
this.handleClickFromParent = this.handleClickFromParent.bind(this);
this.onClose = this.onClose.bind(this);
}
onClose = e => {
this.setState({
show: false
});
};
handleClickFromParent = e => {
this.setState({
show: !this.state.show
});
};
showModal = selectedItem => {
this.setState({
show: !this.state.show,
selectedItem
});
};
setSearchTopStories(result) {
this.setState({ result });
}
componentDidMount() {
fetch(`${PATH_BASE}`)
.then(response => response.json())
.then(result => this.setSearchTopStories(result))
.catch(error => error);
}
render() {
const { searchTerm, result, selectedItem } = this.state;
return (
<div>
{result && result.length
? result.map((item, index) => (
<div>
<div onClick={() => this.showModal(item)}>{item.name}</div>
</div>
))
: null}
{selectedItem && (
<Modal
id={index}
handleClickFromParent={this.handleClickFromParent}
item={[selectedItem]}
show={this.state.show}
onClose={this.onClose}
>
YOLO
</Modal>
)}
</div>
);
}
}
export default Actions;
I started to learn React and I try to make a To do list. I stuck at editing items from the list. This is how I see this. To edit single item I click on the "edit" button, input appears in the place of the item, I submit changes by hiting the "enter" button.
My problem is
my function for submit button don't work (handleEditingDone - in the code).
when I click on the "edit" button TypeError appears
Cannot read property 'task' of undefined
(handleEditing in the code)
App.js
class App extends Component {
constructor(props) {
super(props);
this.state = {
todos: [
// task: '',
// id: '',
//completed: false,
// all of our to-dos
],
todo: '',
// an empty string to hold an individual to-do
filtertodos: []
}
}
inputChangeHandler = event => {
this.setState({[event.target.name]: event.target.value})
}
addTask = event => {
event.preventDefault();
let newTask = {
task: this.state.todo,
id: Date.now(),
completed: false,
editing: false
};
this.setState({
todos: [...this.state.todos, newTask],
todo: ''
})
}
handleEditing = (id) => {
const todos = this.state.todos.map(todo => {
if (todo.id === id) {
todo.editing = !todo.editing
}
return todo
});
this.setState({
todos : todos,
todo: {
changedText: this.todo.task,
}
})
}
handleEditingDone = event => {
if (event.keyCode === 13) {
this.setState(prevState => ({
todo: { // object that we want to update
editing: false, // update the value of specific key
}
}));
}
}
handleEditingChange = event => {
let _changedText = event.target.value;
this.setState({
todo: {changedText: _changedText}
});
}
componentDidMount() {
this.setState({
todo: {changedText: this.todo.task}
})
}
Todo.js
const Todo = props => {
return (
<div style={{display: "flex"}}>
{props.todo.editing ? (
<input onKeyDown={event => props.handleEditingDone} onChange={event => props.handleEditingChange}
type="text" value={props.changedText}/> )
:
(<p key={props.todo.id}
onClick={event => {
props.toggleComplete(props.todo.id)
}} >{props.todo.changedText}{props.todo.completed && ' Completed'}</p>)
}
<button onClick={event => props.handleEditing(props.todo.id)}>EDIT</button>
<button onClick={event => {
props.onDelete(props.todo.id)
}}>x
</button>
</div>
)
}
The problem is the following:
this.todo.task
There is no this.todo.
Try
this.state.todo
I am trying to close a modal when a user presses outside of the Modal element. Somehow when Dismiss() is called, the state is still the same in the callback.
Why is this happening?
export default class Message extends React.Component {
constructor(props) {
super(props);
this.state = {
id: "",
show: false
};
}
componentDidMount() {
this.props.onRef(this);
}
Show(id) {
this.setState({
id: id,
show: true
});
}
Dismiss() {
this.setState({
id: '',
show: false
}, function (state) {
console.log(state) // undefined
});
}
render() {
if (this.state.show) {
return (
<Modal close={() => this.Dismiss()}>
<h1>{this.state.id}</h1>
</Modal>
);
} else {
return null
}
}
}
Not sure why there's a state argument in your callback, should just be
Dismiss() {
this.setState({
id: '',
show: false
}, function () {
console.log(this.state)
});
}
Yes, that is because this.setState function in React is async. And the new state is only available in the event queue
Here is what I mean:
this.setState({newAdded: "test"});
let youCannotGetIt = this.state.newAdded; // here is undefined, because this.setSate is async
This is the code. No idea as to why there is a problem.
class TeacherForm extends Component {
constructor({ data }) {
super();
this.isUpdatingForm = !! data;
this.state = Object.assign({ ... });
this.handleSubmit = this.handleSubmit.bind(this);
this.removeTeacher = this.removeTeacher.bind(this);
}
handleChange(value, field) {
this.setState({ shouldUpdate: true, [field]: value });
}
handleSubmit(e) {
e.preventDefault();
const { name, subjects, parttime, timing } = this.state;
if (this.isUpdatingForm) {
return update.call({
_id: this.props.data._id,
transaction: { name, subjects, parttime, timing },
}, () => this.setState({ shouldUpdate: false }));
}
return add.call();
}
removeTeacher() {
return remove.call(this.props.data._id);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
...
</form>
);
}
}
The error gets thrown at the handleSubmit method in the callback of update.call. This normally shows up when I call removeTeacher and a list updates and this component unmounts.
It sounds like the callback () => this.setState({ shouldUpdate: false }) is executed after that the component is unmounted. Is that possible? If so, one way to get around that is to replace this part by
return update.call({
_id: this.props.data._id,
transaction: { name, subjects, parttime, timing },
}, () => { !this.unmounted && this.setState({ shouldUpdate: false }); });
and to add
componentWillUnmount() {
this.unmounted = true;
}