React, trying to use setState only once - reactjs

Is there any other good way to use setState() only once?
onSearchSubmit = async input => {
const response = await weather.get('/weather', {
params: {
q: this.state.city || input,
},
});
this.setState({ weather: response.data, input: '', city: '' });
};
And then in render function:
this.state.city ? this.onSearchSubmit() : this.onSearchSubmit

Related

This.props doesn't load data from react/redux

I have the following method:
const getAgentData = () => {
axios.get('http://localhost:3000/api/agent', {
headers: {
'Content-Type': 'application/json'
}
})
.then(async res => {
const dbData = res.data.data;
let dataForTable = dbData.map( el => {
let obj = {};
obj._id = el._id;
obj.name = el.name;
obj.phone = el.phone;
if(el.name) {obj.email = el.email}
return obj;
})
await dispatch({ type: 'ADD_PHOTOGRAPHERS', dataForTable });
})
.then(() => {
setLoading(false)
})
.catch((error) => {
console.error(error)
})
}
I update the redux state with this line: await dispatch({ type: 'ADD_PHOTOGRAPHERS', dataForTable });
in a child component I run this:
componentDidMount() {
console.log(this.props.photographers)
}
In the original state this.props.photographers = [];
this is what is logged to the console, it never logs the udpated redux state. From my understading redux should automatically update and it should console.log the udpated state. Any idea what I'm doing wrong?
I also tried logging data with the props being here but it's also an empty array:
class DataTableComponent extends Component {
constructor(props) {
super(props)
this.state = {
data: this.props.photographers,
loading: false,
name: '',
phone: '',
email: '',
}
}
...
My redux map to props in the child component I'm describing is:
function mapStateToProps(state) {
return {
photographers: state.Customizer.photographers
}
}
export default connect(mapStateToProps)(DataTableComponent);
Check if await dispatch({ type: 'ADD_PHOTOGRAPHERS', dataForTable });, seems that you are not sending the payload correctly.
You can log or debug how this action payload data is coming to its reducer.

how can I retrieve the updated state element in componentDidMount in reactjs

I'm trying to retrieve the updated state value in componentDidMount() to append that value as a parameter in fetching data from an API. I have created this handleChange() method from where I can update the state with current required values. The problem is componentDidMount() hits before the first rendering of page so it shows me null value if I try to fetch the state value. I do not know how can I mention that I want to fetch the value after second rendering.
constructor(props) {
super(props)
this.state = {
names: [],
equipments: [],
isLoaded: false,
inputValue: null,
siteID: ''
};
}
handleChange = selectedOption => {
let { inputValue, stateID } = this.state;
this.setState({ ...this.state, inputValue: selectedOption });
console.log(`Option selected: ${selectedOption.value}`);
let selectedElement = selectedOption.value;
let filteredID = stateData.filter(name => name.name == selectedElement)
.map((name) => {
return name.id
})
// console.log(filteredID[0]);
this.setState({ stateID: filteredID[0] })
localStorage.setItem(stateID, filteredID[0]);
};
ComponentDidMount() {
const token = localStorage.getItem("token");
console.log("Inside Component Drop Down =" + token);
// let stateid = this.state.stateID;
// console.log("stateid" + stateid);
let stateid = 23301;//
let url2 = `https://applicaiton/api/helpdesk/get_personID/?stateid=${stateid}`;
fetch(url2, {
method: "GET",
headers: { "Content-Type": "application/json", "Authorization": `Token ${token}` },
credentials: "same-origin"
})
.then((results1) => {
return results1.json();
}).then(data2 => {
this.setState({
isLoaded: true,
equipments: data2,
})
});
}
Rather than componentDidMount, consider using the React lifecycle method componentDidUpdate as follows:
componentDidUpdate(prevProps, prevState, snapshot) {
// Make comparison between this.state and prevState
// as necessary to retrieve proper value
}

Difference between .then and async/await in React

I need to PUT some data to backend, GET a response and use it to setState(), rendering the page syncronously. When using .then the changes made in editPost persist in about half of the time while using async/await seems to work fine(based on experimentation). I'm not sure why this happens since I've been told the two were equivalent:
.then:
onEditSubmit(e){
e.preventDefault()
const newPost = {
id: this.state.id,
title: this.state.titl,
author: this.state.auth,
content: this.state.cont
}
editPost(newPost)
.then(axios.get('/blog')
.then(response => {
this.setState({
posts: response.data,
titl: '',
auth: '',
cont: ''
})
}))
}
async/await:
async onEditSubmit(e){
e.preventDefault()
const newPost = {
id: this.state.id,
title: this.state.titl,
author: this.state.auth,
content: this.state.cont
}
await editPost(newPost)
var response = await axios.get('/blog')
await this.setState({
posts: response.data,
titl: '',
auth: '',
cont: ''
})
}
editPost:
export const editPost = editPost => {
return axios.put(`/blog/write/${editPost.id}`, {
title : editPost.title,
author : editPost.author,
content : editPost.content
})
}
Note: I'm not sure if the fact that setState() being async has to do with this.
You should have a callback function in the first promise:
onEditSubmit(e){
e.preventDefault()
const newPost = {
id: this.state.id,
title: this.state.titl,
author: this.state.auth,
content: this.state.cont
}
editPost(newPost)
.then(() => {
axios.get('/blog')
.then(response => {
this.setState({
posts: response.data,
titl: '',
auth: '',
cont: ''
})
})
})
}

Add array of images to Firebase storage and realtime database using React JS

I am trying to push an array of local images to Firebase store and my database. The images are being outputted in my database json scheme but nothing is showing up in storage and keep receveing the following errors below. Any thoughts?
Error:
Database JSON scheme:
{
"users" : {
"XhLxS1KUS8UyHjsuHYrEuyipQX53" : {
"Email" : "ssssss#gmail.com",
"code" : "bob",
"image1" : {
"id" : "223d7f60-331b-11e9-b680-6b36b34d4cc6",
"url" : "holder1.png"
},
"image2" : {
"id" : "223da670-331b-11e9-b680-6b36b34d4cc6",
"url" : "holder2.png"
},
"image3" : {
"id" : "223da671-331b-11e9-b680-6b36b34d4cc6",
"url" : "holder3.png"
},
"location" : "fl"
}
}
}
React JS:
const images = [
{
id: uuid(),
url: `holder1.png`
},
{
id: uuid(),
url: `holder2.png`
},
{
id: uuid(),
url: `holder3.png`
}
];
class Register extends Component {
state = {
email: '',
password: '',
code: 'bob',
location: 'fl',
image: null,
url: '',
error: null,
arr: images,
};
handleInputChange = e => {
this.setState({ [e.target.name]: e.target.value });
};
handleChange = e => {
if (e.target.files[0]) {
const image = this.state.arr;
this.setState(() => ({ image }));
console.log(image)
}
}
handleSubmit = (event) => {
event.preventDefault();
const { email, password, image, url } = this.state;
const storageRef = storage.ref(`images/`);
this.state.image.map((file, index) => {
storageRef
.child(`${file.url}`)
.getDownloadURL().then(url => {
this.setState({ url }); <---Should I set state?
})
});
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then((user) => {
firebase
.database()
.ref('users/' + user.user.uid)
.set({
Email: user.user.email,
code: this.state.code,
location: this.state.location,
image1: images[0],
image2: images[1],
image3: images[2]
})
//console.log(this.state.url)
this.props.history.push('/');
})
.catch((error) => {
this.setState({ error: error });
});
};
....
This works for a single image to storage:
React JS:
class Register extends Component {
state = {
email: '',
password: '',
code: 'bob',
location: 'fl',
image: null,
url: '',
error: null,
};
handleInputChange = e => {
this.setState({ [e.target.name]: e.target.value });
};
handleChange = e => {
if (e.target.files[0]) {
const image = e.target.files[0];
this.setState(() => ({image}));
}
}
handleSubmit = (event) => {
event.preventDefault();
const { email, password, image, url } = this.state;
const uploadTask = storage.ref(`images/${image.name}`).put(image);
uploadTask.on('state_changed', () => {
storage.ref('images').child(image.name).getDownloadURL().then(url => {
console.log(url);
this.setState({url});
})
});
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then((user) => {
firebase
.database()
.ref('users/' + user.user.uid)
.set({
Email: user.user.email,
code: this.state.code,
location: this.state.location,
image: this.state.url
})
this.props.history.push('/');
})
.catch((error) => {
this.setState({ error: error });
});
};
...
As I commented on your previous question:
You need to write the URL to the database from within the callback to getDownloadUrl(). So where you now call this.setState({url});, call something like firebase.database().ref('users/' + user.user.uid + '/image').set(url); too.
In addition, as far as I can see from the documentation, there are three callbacks for UploadTask.on('state_changed' and the third is called when the upload is completed.
So:
uploadTask.on('state_changed', function(snapshot) {
// handle progress messages here
},(error) => {
// handle errors here
},() => {
storage.ref('images').child(image.name).getDownloadURL().then(url => {
console.log(url);
this.setState({url});
firebase.database().ref('users/' + user.user.uid + '/image').set(url);
})
});

React-Redux: How do I set the initial state from the response of an asynchronous AJAX call?

How to setState() the response received from an AJAX Request so that I can display them in the page?
constructor(props)
{
super(props);
this.state = {
email: '',
first_name: '',
middle_name: '',
country: '',
country_code: '',
mobile_number: '',
gender: ''
}
}
componentDidMount()
{
store.dispatch(getUserProfile())
.then(() => {
const user = this.props.userProfile.userProfile && this.props.userProfile.userProfile.data.data;
this.setState({
email: user.email,
first_name: user.first_name
});
})
}
render()
{
return (
<div className="form-group col-sm-12">
<label htmlFor="email">Email*</label>
<input type="email" name="email" value={this.state.email || ''}/>
</div>
<div className="form-group col-sm-12">
<label htmlFor="email">First Name*</label>
<input type="email" name="email" value={this.state.first_name || ''}/>
</div>
)
}
Apparently, I can't use .then() with store.dispatch method.
Uncaught TypeError: _store2.default.dispatch(...).then is not a function
getUserProfile() action function
import axios from 'axios';
export function getUserProfile()
{
return function(dispatch)
{
dispatch(userProfileSuccess(false));
dispatch(userProfileError(null));
const request = axios
({
url: "http://testapi/auth/v1/user/details",
method: "get",
headers: {
'Content-Type': 'application/json',
'Authorization' : 'Bearer ' + localStorage.getItem('access_token')
}
})
.then(function(response) { dispatch(userProfileSuccess(response)); })
.catch(function(error) {
console.log(error)
});
return {
type: 'USER_PROFILE_SUCCESS',
payload: request
}
};
}
function userProfileSuccess(userProfile)
{
return {
type: 'USER_PROFILE_SUCCESS',
userProfile: userProfile
};
}
function userProfileError(userProfileError)
{
return {
type: 'USER_PROFILE_ERROR',
userProfileError: userProfileError
};
}
export default getUserProfile;
In the AJAX call, I tried:
.then(function(response) {
return new Promise((resolve) => {
dispatch(userProfileSuccess(response));
resolve();
});
})
but the console reports the same error.
Is there a callback that I can pass to store.dispatch? What is the correct approach to this?
You can add a callback in componentDidMount()
componentDidMount()
{
store.dispatch(getUserProfile(), () => {
const user = this.props.userProfile.userProfile && this.props.userProfile.userProfile.data.data;
this.setState({
email: user.email,
first_name: user.first_name
});
})
}
This may not run exactly same, I just want to give you an idea how to add callback using arrow function so that you don't need to use then.
As you are using redux then your redux store should keep track about when the api call is in progress or has completed or caught some error. So instead of passing any callback or promise, you should dispatch an action for each event like processing, success, error etc (which you are already doing in getprofile function). Though i would say you nicely distinguish between process, success, error. For example you getprofile method should roughly look like this
export function getUserProfile() {
return function (dispatch) {
dispatch(userProfileProcessing())
const request = axios({
url: "http://testapi/auth/v1/user/details",
method: "get",
headers: {
'Content-Type': 'application/json',
'Authorization' : 'Bearer ' + localStorage.getItem('access_token'),
},
})
.then(function (response) {
dispatch(userProfileSuccess(response))
})
.catch(function (error) {
dispatch(userProfileError(response))
console.log(error)
});
};
}
It is just what i prefer. If you want it your way, that is also fine.
Now everytime you dispatch any action, redux will update the reducer state. So thats the place where you can set/reset some flag to make the component aware of what is going on with api call. So your reducer might look like this:
// getUserProfileReducer.js
userProfileReducer = (state = {}, action) => {
switch (action.type) {
case 'USER_PROFILE_PROCESSING':
return {
...state,
processing: true,
success: false,
fail: false,
userProfile: null,
}
case 'USER_PROFILE_SUCCESS':
return {
...state,
processing: false,
success: true,
fail: false,
userProfile: action.userProfile,
}
case 'USER_PROFILE_Error':
return {
...state,
processing: false,
success: false,
fail: true,
userProfile: null,
}
}
}
Now all you need to do is to access this state from you component so that you can take necessary action according to that. For that you can user mapStateToProps function which convert the redux state to prop of the component.
constructor(props) {
super(props)
this.state = {
email: '',
first_name: '',
middle_name: '',
country: '',
country_code: '',
mobile_number: '',
gender: '',
}
}
componentWillReceiveProps(newProps) {
if (newProps.userProfileStatus.success) {
// The success flag is true so set the state
const user = newProps.userProfileStatus
this.setState({
email: user.email,
first_name: user.first_name,
})
}
else if (newProps.userProfileStatus.processing) {
// Api call is in progress so do action according to that like show loader etc.
}
}
componentDidMount() {
store.dispatch(getUserProfile())
}
render() {
return (
...
)
}
const mapStateToProps = (state) => {
return {
userProfileStatus: state.userProfileReducer,
}
}
Redux stores the state in the Redux store, separately from the React component state (think setState). You are almost there. What you need to do is guide the result data from the async dispatch to the redux store and then to your local component state. Steps 3 and 4 below.
Dispatch an async action to fetch the data.
Dispatch an action from within the promise to populate the redux state.
Write a reducer that intercepts the action and populates the redux state.
Connect your local component state with the redux state by using the connect function.

Resources