I am newbie in react and I have some trouble that I'd like to solve.
I would like to know how can I show and hide react components before and after to do a rest call.
I have the follow component:
class Loading {
render(){
return (
<div >
<Modal isOpen={true} centered >
<ModalHeader>Loading...</ModalHeader>
<ModalBody >
<div align='center' className="mt-2 mb-2">
<Spinner style={{ width: '4rem', height: '4rem' }} color="primary" />
</div>
</ModalBody>
</Modal>
</div>
)
}
}export default Loading;
And I would like to show this component in other module before to call a rest api and hide this component after the data come. The ideia is some like this:
class List extends Component {
constructor(props) {
super(props)
this.state = {
show : false
}
}
// HERE I WOULD LIKE TO SHOW THE LOADING COMPONENT
callRestApi = () => {
axiosAuth.get(url, getConfig())
.then(response => {
console.log(response.data)
this.setState({
eventos: response.data
})
}).catch(error => {
console.log(error);
return null
});
// HERE I WOULD LIKE TO HIDE THE LOADING COMPONENT
}
render() {
return(
<div>
<Button className="mr-2" color="primary" size="sm" onClick={this.callRestApi}>List All</Button>
</div>
How can I do it?
You can create state that dictates whether the loading spinner is visible or not. And append one last .then in the promise chain to modify it.
class List extends Component {
constructor(props) {
super(props)
this.state = {
show : false,
loaderVisible: true
}
}
// HERE I WOULD LIKE TO SHOW THE LOADING COMPONENT
callRestApi = () => {
axiosAuth.get(url, getConfig())
.then(response => {
console.log(response.data)
this.setState({
eventos: response.data
})
}).catch(error => {
console.log(error);
return null
}).then(() => {
this.setState({loaderVisible: false });
});
}
render() {
return(
<div>
{
this.state.loaderVisible? <Loading /> : ''
}
<Button className="mr-2" color="primary" size="sm" onClick={this.callRestApi}>List All</Button>
</div>
Then utilize ternary syntax on the spinner to determine visibility.
We use state to implement this. Here is the pseudo code.
class List extends Component {
state = { loading: false }
callRestApi = async () => {
this.setState({ loading: true });
await fetch(...);
this.setState({ loading: false });
}
render() {
<div>
{this.state.loading && <Loading />}
<button onClick={this.callRestApi}>List All</button>
</div>
}
}
Related
I want to use image uploader from AntDesign library. Here is a snapshot
import { Upload, Icon, Modal } from 'antd'
class PicturesWall extends React.Component {
state = {
previewVisible: false,
previewImage: '',
fileList: [],
}
handleCancel = () => this.setState({ previewVisible: false })
handlePreview = file => {
this.setState({
previewImage: file.url || file.thumbUrl,
previewVisible: true,
})
}
handleChange = ({ fileList }) => this.setState({ fileList })
render() {
const { previewVisible, previewImage, fileList } = this.state
const uploadButton = (
<div>
<Icon type="plus" />
<div className="ant-upload-text">Upload</div>
</div>
)
return (
<div className="clearfix">
<Upload
action="//jsonplaceholder.typicode.com/posts/"
listType="picture-card"
fileList={fileList}
onPreview={this.handlePreview}
onChange={this.handleChange}
>
{fileList.length >= 3 ? null : uploadButton}
</Upload>
<Modal
visible={previewVisible}
footer={null}
onCancel={this.handleCancel}
>
<img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal>
</div>
)
}
}
ReactDOM.render(<PicturesWall />, mountNode)
It's so hard to understand what's going on here for me .
Can I get images from this component using something like
const img=event.target.files[0];?
All I want it's put that uploaded images into array and using FormData send axios.post request to the backend.
I'm newbie in React .If it's something obvious pardon me.Thank you in advance
antd's Upload component is doing the upload for you under the hood. But if you don't want to do that, and upload the file later, you can also achieve that with the help of beforeUpload prop.
From the docs:
beforeUpload: Hook function which will be executed before uploading. Uploading will be stopped with false or a rejected Promise returned. Warning:this function is not supported in IE9
I have written an example and added comments where necessary:
class PicturesWall extends React.Component {
state = {
previewVisible: false,
previewImage: "",
fileList: []
};
handleCancel = () => this.setState({ previewVisible: false });
handlePreview = file => {
this.setState({
previewImage: file.thumbUrl,
previewVisible: true
});
};
handleUpload = ({ fileList }) => {
//---------------^^^^^----------------
// this is equivalent to your "const img = event.target.files[0]"
// here, antd is giving you an array of files, just like event.target.files
// but the structure is a bit different that the original file
// the original file is located at the `originFileObj` key of each of this files
// so `event.target.files[0]` is actually fileList[0].originFileObj
console.log('fileList', fileList);
// you store them in state, so that you can make a http req with them later
this.setState({ fileList });
};
handleSubmit = event => {
event.preventDefault();
let formData = new FormData();
// add one or more of your files in FormData
// again, the original file is located at the `originFileObj` key
formData.append("file", this.state.fileList[0].originFileObj);
axios
.post("http://api.foo.com/bar", formData)
.then(res => {
console.log("res", res);
})
.catch(err => {
console.log("err", err);
});
};
render() {
const { previewVisible, previewImage, fileList } = this.state;
const uploadButton = (
<div>
<Icon type="plus" />
<div className="ant-upload-text">Upload</div>
</div>
);
return (
<div>
<Upload
listType="picture-card"
fileList={fileList}
onPreview={this.handlePreview}
onChange={this.handleUpload}
beforeUpload={() => false} // return false so that antd doesn't upload the picture right away
>
{uploadButton}
</Upload>
<Button onClick={this.handleSubmit} // this button click will trigger the manual upload
>
Submit
</Button>
<Modal
visible={previewVisible}
footer={null}
onCancel={this.handleCancel}
>
<img alt="example" style={{ width: "100%" }} src={previewImage} />
</Modal>
</div>
);
}
}
ReactDOM.render(<PicturesWall />, document.getElementById("container"));
I am building an app in React, that is connected to an API I have written before. Buttons are renderizing but all of them change at the same time. I need advice about how can I write my code in order to separate the functionality.
My app renderize with a .map the same number of Buttons as appointments which is an array. All of them change when this.state.shown change but I need to separate all the buttons in order to only show the one that I clicked. Right now, when I clicked in one of them, this.state.shown change its value so all the buttons change because all depends of the same variable. I am looking for advices about how I can separate this.
class AppointmentsList extends Component {
constructor(props) {
super(props);
this.state = {
appointments: [],
isLoading: false,
shown: false, //Variable to know if a button need to change and render the component
customerUp: false
}
this.toggleCustomer = this.toggleCustomer.bind(this);
//this.showCustomer = this.showCustomer.bind(this);
}
toggleCustomer() {
this.setState({
shown: !this.state.shown
})
} //This function change the value of shown when a Button is clicked.
render() {
const {appointments, isLoading} = this.state;
if(isLoading) {
return <p>Loading...</p>;
}
return(
<div>
<h2>Lista de citas</h2>
{appointments.map((app) =>
<div key={app.id}>
<p>Fecha: {app.appointment}</p>
<p>Cliente: {app.customer.name}</p>
<p>Id: {app.customer.id}</p>
{ this.state.shown ? <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ocultar cliente</Button> : <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ver cliente</Button> }
{ this.state.shown ? <CustomerView id={app.customer.id} /> : null }
</div>
)}
</div>
)
}
How can I reorganize my code in order to render the Buttons separately?
Thanks in advance.
Method 1: You can make shown state a object like:
state = {
shown:{}
}
toggleCustomer(id) {
const updatedShownState = {...this.state.shown};
updatedShownState[id] = updatedShownState[id] ? false : true;
this.setState({
shown: updatedShownState,
})
} //This function change the value of shown when a Button is clicked.
render() {
const {appointments, isLoading} = this.state;
if(isLoading) {
return <p>Loading...</p>;
}
return(
<div>
<h2>Lista de citas</h2>
{appointments.map((app) =>
<div key={app.id}>
<p>Fecha: {app.appointment}</p>
<p>Cliente: {app.customer.name}</p>
<p>Id: {app.customer.id}</p>
{ this.state.shown[app.customer.id] ? <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer(app.customer.id) }>Ocultar cliente</Button> : <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ver cliente</Button> }
{ this.state.shown[app.customer.id] ? <CustomerView id={app.customer.id} /> : null }
</div>
)}
</div>
)
}
Method 2: Make a separate component for Button and Customer
return(
<div>
<h2>Lista de citas</h2>
{appointments.map((app) =>
<Appointment key = {app.id} app = {app} />
)}
</div>
)
}
class Appointment extends Component {
state = {
shown: false,
}
toggleCustomer() {
this.setState({
shown: !this.state.shown
})
}
render() {
const { app } = this.props;
return (
<div key={app.id}>
<p>Fecha: {app.appointment}</p>
<p>Cliente: {app.customer.name}</p>
<p>Id: {app.customer.id}</p>
{ this.state.shown ? <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ocultar cliente</Button> : <Button key={app.customer.id} color="danger" onClick={() => this.toggleCustomer() }>Ver cliente</Button> }
{ this.state.shown ? <CustomerView id={app.customer.id} /> : null }
</div>
)
}
}
Let me know if it works and the method you prefer.
You can create a separate component for your button (buttonComponent) inside your AppointmentsList component and pass the shown has props and the in componentDidMount of buttonComponent copy the props to the state of buttonComponent.
This way each button will have its own state, which manages shown.
Button component:
import react from 'react';
interface buttonComponentProps{
shown: boolean;
}
interface buttonComponentState{
shown: boolean;
}
class buttonComponent extends react.Component<buttonComponentProps,{}>{
constructor(props:buttonComponentProps){
super();
this.state{
shown:props.shown
}
}
....
}
export default buttonComponent;
When a user enters a search item, if the data is available, then <Pictures /> is displayed. If the data is not present then <NoResultsFound /> is displayed.By default <NoResultsFound /> state is false and <Pictures /> is true because when the page loads the list of pictures are present. I tried to switch the state like this: this.setState({uisNoResultsFound: true}) and this.setState({uisPictures: false}) throws syntax error. I want this conditional rendering of the UI states within app.js. How to do this?
App.js:
class App extends Component {
constructor(props) {
super(props);
this.state = {
uisSearchBarItems: true,
uisNoResultsFound: false,
uisPictures: true,
dsPictures: []
};
}
componentDidMount() {
unsplash.search.collections("frog", 1, 60)
.then(toJson)
.then(json => {
this.setState({ dsPictures:json.results });
})
}
enteredDatahandler = (ctp) => {
unsplash.search.collections(ctp, 1, 60)
.then(toJson)
.then(json => {
this.setState({ dsPictures:json.results })
})
//******** conditional rendering ***********
if(this.state.dsPictures.length === 0){
return (
this.setState({uisNoResultsFound: true})
this.setState({uisPictures: false})
)
}
else{
this.setState({uisNoResultsFound: false})
this.setState({uisPictures: true})
}
//***********************************
}
render() {
return (
<div className="App">
<SearchBarItems ctpEnteredData={this.enteredDatahandler}/>
<NoResultsFound />
<Pictures ptcEnteredData={this.state.dsPictures}/>
</div>
);
}
}
export default App;
searchbaritems.js
class SearchBarItems extends Component {
enterKeyHandler = (event) => {
if (event.key === 'Enter'){
event.preventDefault();
this.props.ctpEnteredData(this.search.value)
}
}
render() {
return (
<div>
<form autoComplete="off" ref={(el) => this.myFormRef = el}>
<input
type="text"
name="search"
ref={input => this.search = input}
onKeyPress={this.enterKeyHandler}/>
</form>
</div>
)
}
}
Use a ternary expression inside your render method.
{this.state.dsPictures.length === 0 ? <NoResultsFound /> : <Pictures ptcEnteredData={this.state.dsPictures}/> }
In your render function you are returning both components, you need to either have if statements or you can do what #Barazu did - which is the most cleanest code.
Github gist: https://gist.github.com/tintinmovie/ed5b4782fa98c3482b561ea3243f98ea
render() {
if (this.state.uisNoResultsFound === true) {
return(
<div className="App">
<SearchBarItems ctpEnteredData={this.enteredDatahandler}/>
<NoResultsFound />
</div>
);
}
else if (this.state.uisPictures === true) {
return(
<div className="App">
<SearchBarItems ctpEnteredData={this.enteredDatahandler}/>
<Pictures ptcEnteredData={this.state.dsPictures}/>
</div>
);
}
}
What are the solutions for this problem?
<Form.Field>
<label> </label>
<MockMutation mutation={DO_LOGIN}>
{(doLogin, { loading, error, data }) => {
if (!loading && data.loggedInState == "LOGGED_IN") {
// this.setState({goodLogin: true})
// I need to update state here to redirect to other page
// How can I do it with out the annoying warning???
}
return (
<Button
primary
className="login-btn full-width"
disabled={loading}
onClick={e => {
console.log("on click btn clicked");
e.preventDefault();
doLogin({
variables: {
employeeId: this.state.employeeId,
password: this.state.password
}
});
}}
>
<span style={loading ? { display: "none" } : {}}>Login</span>
<span style={loading ? {} : { display: "none" }}>Loading...</span>
</Button>
);
}}
</MockMutation>
</Form.Field>
If you are using react-router.v4 you can use Redirect component to do make a redirect.
if (!loading && data.loggedInState == "LOGGED_IN") {
// this.setState({goodLogin: true})
// I need to update state here to redirect to other page
// How can I do it with out the annoying warning???
return <Redirect to="/some/path" />
}
If you don't use react-router-4 then it is fairly easy to implement such component anyway:
class Redirect extends React.Component {
componentDidMount(){
const { history, to } = this.props;
history.push(to);
}
render() {
return null
}
}
export default withRouter(Redirect);
I'm learning React and I'm trying to make a button which will change on click. It must be ether "Succeed" or "Not succeed" depending on the server's answer. This is what I've done so far. My question is - what the handleClick function must do? Should I use transition by toggling classes?
Thanks!
class Btn extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handlenClick.bind(this);
}
handleClick() {
???
}
render() {
const succeed = (
<div>
<ButtonToolbar>
<Button bsStyle="primary" bsSize="large">Succeed</Button>
</ButtonToolbar>
</div>
)
const notsucceed = (
<div>
<ButtonToolbar>
<Button bsStyle="primary" bsSize="large">Not succeed</Button>
</ButtonToolbar>
</div>
)
return (
<div onClick={this.handleClick.bind(this)}>
{this.state ? succeed : notsucceed}
</div>
)
}
};
You need to implement the state.
For example: https://codesandbox.io/s/313vmr23k6
class Btn extends React.Component {
constructor(props) {
super(props);
this.state = {
succeed: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(previousState => {
return {
succeed: !previousState.succeed
};
});
}
render() {
const succeed = (
<div>
<button bsStyle="primary" bsSize="large">Succeed</button>
</div>
)
const notsucceed = (
<div>
<button bsStyle="primary" bsSize="large">Not Succeed</button>
</div>
)
return (
<div onClick={this.handleClick.bind(this)}>
{this.state.succeed ? succeed : notsucceed}
</div>
)
}
};
You would call your api in there and set the state depending if the call fails or succeeeds. For this your api call must return a Promise. Something like:
handleClick() {
Api.call()
.then((response) => { this.setState({ succeed: true }); })
.catch((error) => { this.setState({ succeed: false }); })
}