I have a problem fetching data from firebase with componentDidMount().
Fetching the data working fine but the problem is when it comes to lifecycle method. I console.log out the fetched data.
it fetched data when the component renders and after it fetched, the data become undefined and my render method appeared and disappeared in 0.001sec.
Do I need to store the props data into the state in order to not to disappear
Thank you for the help!
Action
export const fetchUserAddressbook = () => {
const { currentUser } = auth;
return dispatch => {
database
.ref(`users/${currentUser.uid}/shippingAddress`)
.on('value', snapshot => {
dispatch({
type: FETCH_SHIPPING_ADDRESS_SUCCESS,
payload: snapshot.val()
});
});
};
};
class
componentDidMount() {
this.props.fetchUserAddressbook();
}
render(){
return
(
<div>{this.renderAddressCard(this.props.shippingAddressBook)}</div>
)
const mapStateToProps = state => {
return {
shippingAddressBook: state.user.shippingAddressBook
};
}
renderMethod
renderAddressCard(shippingAddressBook) {
return _.map(shippingAddressBook, (value, uid) => {
return (
<Card header={value.companyName} />
);
});
}
Related
I'm building a small ToDo list app with React Apollo and GraphQL. In order to add a new ToDo item I click "Add" button that redirects me to a different URL that has a form. On form submit I perform a mutation and update the cache using update function. The cache gets updated successfully but as soon as I return to the main page with ToDo list, the component triggers an http request to get the ToDo list from the server. How do I avoid that additional request and make ToDoList component pull data from the cache ?
My AddToDo component:
const AddToDo = () => {
const { inputValue, handleInputChange } = useFormInput();
const history = useHistory();
const [addToDo] = useMutation(ADD_TODO);
const onFormSubmit = (e) => {
e.preventDefault();
addToDo({
variables: { title: inputValue },
update: (cache, { data: { addToDo } }) => {
const data = cache.readQuery({ query: GET_TODO_LIST });
cache.writeQuery({
query: GET_TODO_LIST,
data: {
todos: [...data.todos, addTodo],
},
});
history.push("/");
},
});
};
return (
...
);
};
And ToDoList component
const ToDoList = () => {
const { data, loading, error } = useQuery(GET_TODO_LIST);
if (loading) return <div>Loading...</div>;
if (error || !loading) return <p>ERROR</p>;
return (
...
);
};
Works as expected.
Why unecessary? Another page, new component, fresh useQuery hook ... default(?) fetchPolicy "cache-and-network" will use cached data (if exists) to render (quickly, at once) but also will make request to be sure current data used.
You can force "cache-only" but it can fail if no data in cache, it won't make a request.
After I recieved the data from firebase and store it into the post state and I try to pass each data to another component one one data is been sent to the prop anyone have.
Any idea as to why?
import React, { Component } from 'react';
import BlogPost from './BlogPost'
import firebase from '../../config/fbConfig'
class BlogList extends Component {
state = {
posts: []
}
componentDidMount() {
const db = firebase.firestore()
db.collection('posts').get().then(snapshot => {
snapshot.docs.forEach(post => {
let id = post.id
let postData = post.data()
postData['id'] = id
this.setState({
posts: [...this.state.posts, postData]
})
})
})
}
render() {
console.log(this.state.posts)
return (
<>
{this.state.posts ?
this.state.posts.map(post =>
<BlogPost post={post} key={post.id} />
)
: <h1>loading</h1>}
</>
);
}
}
export default BlogList;
The issue is in how you update the state, you must either use functional setState since you call setState within a loop and use this.state.data to update state, but state updates are not performed immediately but happen asynchronously
componentDidMount() {
const db = firebase.firestore()
db.collection('posts').get().then(snapshot => {
snapshot.docs.forEach(post => {
let id = post.id
let postData = post.data()
postData['id'] = id
this.setState(prev => ({
posts: [...prev.posts, postData]
}))
})
})
}
or even better update once all the data is available
componentDidMount() {
const db = firebase.firestore()
db.collection('posts').get().then(async snapshot => {
const data = snapshot.docs.map(post => {
let id = post.id
let postData = post.data()
postData['id'] = id
return postData;
});
this.setState(prev => ({
posts: [...prev.posts, ...data]
}));
})
}
I have question about dispatch action. I do not know why my dispatch redux run infinitely.
Below is my ListUser component
import { ListUsersAction } from "../actions/ListUsersAction";
const ListUsers = props => {
var resPerPage = configList.users.resPerPage;
props.ListUsersAction(resPerPage, 1);
if (props.listUsersReducer.thanhvien.length > 0) {
const listUsersReducer = props.listUsersReducer;
const propToSend = {
currentPage: listUsersReducer.currentPage,
pages: listUsersReducer.pages,
resPerPage: listUsersReducer.resPerPage
};
return (
<Fragment>
<Pagination pageProp={propToSend} />
</Fragment>
);
} else {
return null;
}
};
const mapStateToProp = state => ({
listUsersReducer: state.listUsersReducer
});
export default connect(mapStateToProp, { ListUsersAction })(ListUsers);
and here is ListUserAction
export const ListUsersAction = (resPerPage, currentPage) => async dispatch => {
if (localStorage.token) {
setAuthToken(localStorage.token);
}
try {
const res = await axios.get('/api/admin/users/page/:page', {
params: {
page: currentPage,
resPerPage: resPerPage
}
});
dispatch({
type: LOADUSERS,
payload: res.data
});
}catch(err){
console.log(err);
dispatch({
type: STOPLOADUSERS
})
}
}
You can see the action always render
Can you tell me why and how to fix it?
You are calling your action every time your Component re renders, and calling your action is causing your Component to re render, creating an infinite loop.
Put your action inside a useEffect to prevent this and only call it once on component mount or whenever you want based on the dependency array:
useEffect(() => {
var resPerPage = configList.users.resPerPage;
props.ListUsersAction(resPerPage, 1);
},[])
const ListUsers = props => {
React.useEffect(()=>{
var resPerPage = configList.users.resPerPage;
props.ListUsersAction(resPerPage, 1);
},[])
// your code
};
try this
functional component render every times,
thats why it happend
check hooks API useEffect
I am using react with redux and typescript, Trying to add item from react via api call which returns back whether it is success or failed.
So I fetch data from componentDidMount
componentDidMount() {
this.props.dispatch(loadData());
}
And from the actions layer I add item like below
static addItem = (Item: IAddItemRequest): Promise<number> => {
return Promise.resolve(
AI.addItem(Item).then((ItemData) => {
return ItemData;
}).catch(error => {
return error;
}));
}
So how can I loadData after Adding an Item
Also AddItem doesn't change in the state
A possible soln can be opted using two actions, 1 that add item and second that load data.
export const loadData = (ItemData) => {
return {
type: LOAD_DATA,
payload: ItemData
};
};
export const addItemAction = (Item) => {
return dispatch => {
addItem(Item).then(ItemData => {
dispatch(loadData(ItemData));
});
};
};
Above both are inter linked redux actions and we have to call first action in react component's componentDidMount method as follow
componentDidMount() {
this.props.dispatch(addItemAction(Item));
}
What I want to do is show the items saved in AsyncStorage in a FlatList.
savePosts = async () => {
try {
let post = {
postId: '1',
postTitle: 'This is an example',
}
const posts = await AsyncStorage.getItem('posts') || '[]';
posts = JSON.parse(posts);
posts.push(post);
AsyncStorage.setItem('posts', JSON.stringify(posts)).then(() => {
});
} catch(error) {
}
};
You can use state and initialise it in componentDidMount:
state = {
data: [],
}
componentDidMount() {
AsyncStorage.getItem('posts').then(data => this.setState({ data }));
}
<FlatList data={this.state.data} ...otherProps />
Alternatively, if you use Redux, you can call AsyncStorage.getItem anywhere and dispatch an action to populate the store, which the FlatList component can connect to and display the data.