Let's fetch data from jsonplaceholder and set a state with useState() hook upon success or error, which will lead to a render. We put artificial 1s delay function between request and response to be able to see a state change on a screen, otherwise it happens too fast. function sleeper(ms) { return function(x) { return new Promise(resolve => setTimeout(() => resolve(x), ms)); }; } Jsonplaceholder returns 100 items, so every time we ask data under number 101 and more we get an error for our http request. import axios from 'axios' import randomNumFromTo from '/functions/randomNumFromTo'; import sleeper from '/functions/sleeper'; function ComponentWithUseState() { const [state, setState] = useState({ loading: false, errorMsg: '', title: '', postNum: -1 }) function getTitle() { const postNum = randomNumFromTo(1, 150) const url = `https://jsonplaceholder.typicode.com/posts/${postNum}` setState({ ...state, loading: true, postNum: postNum, errorMsg: '' }) axios(url) .then(sleeper(1000)) .then(res => setState({ loading: false, errorMsg: '', title: res.data.title, postNum: postNum })) .catch(() => setState({ loading: false, errorMsg: 'ERROR', title: '', postNum: postNum })) } return ( <> <button onClick={getTitle}>Get post titles</button>&emsp; Post #{state.postNum} &emsp; Title: {state.loading ? 'Loading...' : state.title} &emsp; <span style={{ color: 'red' }}>{state.errorMsg}</span> </> ) }