I have written a method like below but i am noticing my API is getting called multiple times.
I am calling my API inside componentDidMount().
class User extends Component {
state = {
cardData: [],
};
userDetails = async () => {
const data = await fetchUserDetails();
if (data) {
const url = data.baseUrl;
const getDetails = Object.values(data).map((users) => users.user);
this.setState({ cardData: getDetails });
}
};
componentDidMount() {
this.userDetails();
}
}
How can i overcome this?
Related
I have a screen like that
const ScreenA = observer(() => {
const { userStore } = useRootStore();
useEffect(() => {
setInterval(() => {
userStore.ping();
}, 3000);
}, []);
console.log(userStore.pingResult);
retunr <></>
});
And store
class UserStore {
pingResult = null;
ping = async () => {
console.log('ping');
const pingResult = await this.userService.ping();
runInAction(() => {
this.pingResult = pingResult;
});
};
}
The problem is console.log(userStore.pingResult); works only once — I guess, observer just can't see that store has been updated. Tried to JSON.parse(JSON.stringify(pingResult)) to make sure that pingResult is completely new, but it didn't work. At the same time console.log('ping') works as expected — every 3 seconds.
I think you have to make your user store observable. Otherwise it is just a plain JS instance that is not observable for mobx
class UserStore {
constructor() {
makeAutoObservable(this);
}
// ...
}
I am trying to use the search api of Open Library.
Usually, if you are going to use a functional component, you will do it like this on your api file:
import axios from 'axios';
export default axios.create({
baseuRL: 'http://openlibrary.org/search.json'
})
And then you will import this on a file where you will fetch the data:
import booksAPI from '../apis/books';
const books = () => {
useEffect(() => {
books()
}, [])
const books = async() => {
const res = await booksAPI.get('?author=tolkien');
console.log(res.data);
}
}
This is expected to console.log the data on your terminal. However, using the class component with axios and componentDidMount to fetch the data.
class BookList extends Component {
constructor(props) {
super(props)
this.state = {
books: []
}
}
componentDidMount(){
const booksResponse = async() => {
const response = await booksAPI.get('?author=tolkien');
console.log(response.data)
}
}
This is complaining about the await keyword and doesn't console.log the data. Also, I am not sure how I can convert the useEffect to a class component so it can perform side effects?
const BookList = () => {
// State variable where you can store the data
const [books, setBooks] = useState([]);
// Effect, which would be called on component mount because of empty array of dependencies -> [] (look for useEffect lifecycles) and set the data to state variable. After this, component will re-render.
useEffect(() => {
const fetchBooks = async () => {
const response = await booksAPI.get('?author=tolkien');
setBooks(response.data);
console.log(response.data);
}
fetchBooks();
}, []);
return ...
}
Also, make sure that you're adding query/mutation/subscription to your GraphQL document right before the name of query/mutation/subscription, as someones told you in the comment.
In case you're looking for class component realization, your code should look like this:
class BookList extends Component {
constructor(props) {
super(props)
this.state = {
books: []
}
}
fetchBooks = () => {
const response = booksAPI.get('?author=tolkien');
this.setState({ books: response.data });
}
componentDidMount(){
fetchBooks();
}
...
}
Here I have created a simple example in React for you.
componentDidMount() {
this.booksResponse();
}
booksResponse = async () => {
const response = await axios.get(
'https://openlibrary.org/search.json?author=token'
);
console.log(response.data);
};
Here is Stackblitz link.
https://stackblitz.com/edit/react-zk8nhq
If you find any confusing then Please comment here I can create more example for you.
How do I convert this class component to a functional component?
What I am trying to achieve is to subscribe and unsubscribe from firebase using useEffect()
class PostsProvider extends Component {
state = { posts: [] }
unsubscribeFromFirestore = null;
componentDidMount = () => {
this.unsubscribeFromFirestore = firestore
.collection('posts')
.onSnapshot(snapshot => {
const posts = snapshot.docs.map(collectIdAndDocs);
this.setState({ posts });
});
}
componentWillUnmount = () => {
this.unsubscribeFromFirestore();
}
This is how I'd convert your component. You'd useState() to create your posts state and then a useEffect is pretty straightforward to move. The main thing you'd want to make sure of is that your dependency array is correct for it so it doesn't subscribe and unsubscribe too often (or not often enough).
function PostsProvider(){
const [posts,setPosts] = useState([]);
useEffect(() => {
const unsubscribeFromFirestore = firestore
.collection('posts')
.onSnapshot(snapshot => {
const posts = snapshot.docs.map(collectIdAndDocs);
setPosts(posts);
});
return () => {
unsubscribeFromFirestore();
}
}, [])
}
The way I cache data in Class component is like below :
1. make async API call in componentDidMount
2. get API response and dispatch data through redux
3. use response data by mapping state to prop
What I want to do is caching data right after you get API response with mapped redux state value
inside of useEffect in function component
(
It works on class component. but I'm wondering how should I make it work in function component)
export class MyClassComponent extends React.Component {
private readonly _myCachedData = {
'data1': {}
}
public async componentDidMount() {
await this.loadAsyncData();
}
private loadAsyncData() {
const { makeAPICall } = this.props
await makeAPICall();
return this._myCachedData.data1 = this.props.data1FromReduxConnect;
}
}
export const mapStateTopProps = (state) => {
const { data1FromReduxConnect } = state;
return data1FromReduxConnect;
}
...
What I have tried :
export const MyFunctionComponent = props => {
const { data1FromReduxConnect } = props;
const myCachedData = React.useRef();
const loadAsyncData = () => {
const { makeAPICall } = this.props
await makeAPICall();
return myCachedData.current = data1FromReduxConnect;
}
React.useEffect(()=> {
await loadAsyncData();
})
}
export const mapStateTopProps = (state) => {
const { data1FromReduxConnect } = state;
return data1FromReduxConnect;
}
I was only able to get the previous value ofdata1FromReduxConnect unlike class component did get updated value this.props.data1FromReduxConnect after API call
Not sure if I should just keep class component for it, or is there a way to deal with this issue!
Thanks!!
I don't think that is the right way to use the useRef hook. Similar to React's class components' createRef(), it is actually used to access the DOM in functional components.
If the HTTP request happens only once when MyFunctionComponent is initialised, we can use [] as the second argument in the useEffect hook which will cause this effect to be run only once. In addition, we will need to make use of useState hook to keep track of the component's state which is to be updated with the values from the redux store.
export const MyFunctionComponent = props => {
const { data1FromReduxConnect } = props;
const [ myData, setMyData ] = useState();
const loadAsyncData = async() => {
const { makeAPICall } = this.props
await makeAPICall();
}
useEffect(()=> {
async function getData() {
await loadAsyncData();
}
getData();
// do the rest to get and store data from redux
setMyData(data1FromReduxConnect);
}, [])
}
I am using thunk middleware and I have two asynchronous actions creators like following.
export const fetchObject = () => {
return dispatch => {
let action = fetchObjectRequest();
dispatch(action);
let url = "URL1";
let promise = axios.get(url)
.then(response => dispatch(fetchObjectSuccess(response.data)));
return handlingErrorsPromise(promise,
error => {
console.error(JSON.stringify(error));
dispatch(errorOccurred(action, error))
}
);
}
};
Let's assume I have Object1 and Object 2 endpoints, but the problem is Object1 is required by almost all components and I have to somehow merge all other objects with data from Object1.
Ex: Object2 contains peoples id and I have to attach them names from Object1.
Currently I am mapping my component properties to both objects, and I have if statements in render checking if all object are fetched. Like this:
class Peoples extends Component {
componentWillMount(){
this.props.fetchObject1();
this.props.fetchObject2();
}
render() {
let peoples = this.mergeObjects();
//rendering
}
mergeObjects = () => {
let isFetching = this.props.object1.isFetching ||
this.props.object2.isFetching;
if (isFetching) {
return {
isFetching,
json: []
};
}
let mergedJson = {...};
return {
isFetching,
json: mergedJson
};
};
}
const mapDispatchToProps = dispatch => {
return {
fetchObject1: () => dispatch(fetchObject1()),
fetchObject2: () => dispatch(fetchObject2())
}
};
const mapStateToProps = state => {
return {
object1: state.object1,
object2: state.object2
};
};
export default Peoples = connect(mapStateToProps, mapDispatchToProps)(Peoples);
Is there a more elegant way to merge one asynchronous object with others in my store?
I believe you could use the Promise.all api since axios returns a promise.
Promise.all([this.props.fetchObject1(), this.props.fetchObject2()]).then((data) => {
//do stuff with data
})
Update:
You can trigger an action in your view this.props.action.fetchObjects() that does the Promise.all or yield all[] if you're using redux-saga and then trigger another action that updates your store with both of them at once (merged or not). You can easily merge them in your view or even in the selector function.