API call using React Hooks - reactjs

I am trying to fetch an api with react hooks. I am able to get the data from the api and declared some attributes with useState. Now I am trying to set the attributes from the api to the attributes in useState, the same way you would do it with setState in class components, but I am not sure how to go about it.
Above is the userData that I did a console.log for in the promise.

useState returns an array where the first element is the data and the second is a function which you call anywhere to update the state later.
From your example:
// Receive the array from useState and use the "array destructuring"
const [myState, setMyState] = useState({/*... you data */});
// ...
useEffect(() => {
// fetch...
// .then...
.then(userData => {
console.log(userData);
setMyState(userData); // call the function here
});
// ...
This should be enough to have your state object updated!

You need to make a few changes, first is that the initial state should be an array
const [initialState, setInitialState] = useState([])
I would use an array since the response is giving you an array of users, in your then statement where you have the console.log you can do
setInitialState(userData)
That will update the initialState value with the array of items, if you actually plan to use just one user just pick one from the userData array. The setInitialState will be the function you need to use when you plan to use the initialState value.

Related

Why doesn't my state change when the category on useParams changes?

const { category } = useParams();
const [sort, setSort] = useState(category);
This think is really driving me crazy - when i console the category, it is actually changing, but sort state doesn't..
You need to do some reading in order to understand how React Hooks work. There are many tutorials online - the React API documentation is even a good start:
What do we pass to useState as an argument? The only argument to the useState() Hook is the initial state.
From the React docs. Notice they say the argument is the initial state only.
When you do useState(X) - the value X is only used the first time the component is rendered. On subsequent renders, if and when X changes, and you want to use the new X, you must update the state value yourself via the set function returned by useState.
In your case, we can say that the state variable changes whenever category changes, so category is a dependency of the state variable. When you have a dependency of this kind, you know you need to use an effect, by way of the useEffect hook:
const { category } = useParams();
const [sort, setSort] = useState(category);
// This callback will run whenever the dependencies
// specified in the second argument change.
useEffect(() => {
setSort(category)
}, [category]) // <= the dependencies
You are expecting whenever category changes, sort should also changes, if this is the goal, try this:
useEffect(() => setSort(category), [category]);
if you only want to change sort first time on initial render, try this:
useEffect(() => setSort(category), []);
For more info, see Using the Effect Hook - React

React + Redux - Update local state on store change => infinity loop

I have an array that resides in store, let's call it users and I have a custom hook that retrieves it from store (via useSelector). Based on this array in the store, I have a local state in my component that I use as the display source of a table. I need to have it separately in local state since I filter the data based on a search functionality.
What I want to do is, every time when the data in the store changes, I want to update the local state as well to reflect those changes (but call the filtering function on top of it before hand).
This however results in an infinity loop since the useEffect + setState cause a redraw which changes the store variable again etc.
const users = useUsers() // uses redux store
const [displayUsers, setDisplayUsers] = useState(users) // local state
useEffect(() => {
const filteredUsers = onSearch(users) // applies the filtering
setDisplayUsers(filteredUsers) // updates the local state
}, [users])
return <Table source={displayUsers} ... />
Can anybody help?
Since displayUsers is derived from the props, you don't need to maintain it in the state. Calculate it on the fly whenever the props changes, and use memo if the component is rendered often for other reasons:
const users = useUsers() // uses redux store
const displayUsers = useMemo(() => onSearch(users), [onSearch, users])
return <Table source={displayUsers} ... />
If you really need to set the state when the store changes, make sure that the selector is memoized - ie if nothing changes you'll get the same array. So when re-rendering the component, the selector would return the same users array, and this will prevent the useEffect from being called again.
You can call useSelector with the equalityFn (2nd param) to avoid returning a new value if nothing changed (see Equality Comparisons and Updates). This is the example from the docs:
import { shallowEqual, useSelector } from 'react-redux'
const selectedData = useSelector(selectorReturningObject, shallowEqual)

Can the updater function in useState be redefined in React?

I'm working on a rather complex component that uses a store object. The data is local, so it doesn't warrant being added to my Redux store, but expensive enough that it shouldn't be calculated every render. I'm using useState to store and update this object. However, I have several functions regarding the store that I'd like to break out into a new file, including the updater function. For example, I'd like to do something akin to this:
import { storeUpdater } from './ComponentStore.js';
function MyComponent(props) {
const updateStore = storeUpdater;
let storeState = useState({});
const store = storeState[0];
storeState[1] = updateStore;
...
}
Would this reliably work, and, more importantly, is it breaking any rules/anti-patterns?
This shouldn't work. You're just re-assigning storeState[1], which you defined, to a different function, other than setState provided by useState. This code shouldn't update your storeState[0] at all.
Instead, you should make your storeUpdater function take setState function as an argument and provide it in this component.
function storeUpdater(setState) {...}
const updateStore = () => storeUpdater(storeState[1])
And then, inside your updateStore, do the desirable modifications to the storeState and then pass the new state to setState. If the new state depends on the previous state, you can use setState(prevState => nextState) syntax.

Setting Redux state as a default state when using React Hooks

I have a redux action that fetches all data and stores it into a global Redux store.
I want to store that state in a local state using Hooks so that the actual state doesn't get changed when I change it locally.
What I am doing right now is,
const [filteredTable, setFilteredTable] = useState([])
useEffect(() => {
props.fetchDatabase();
props.fetchOptions();
setFilteredTable(props.filtered_table_data);
}, [])
In useEffect, the props.fetchDatabase() gets the props.filtered_table_data and I can see that when I console.log it out.
However, when I use Hooks to store it into a local state and check if it's in there,
console.log(filteredTable, 'filteredTable')
just gives me [].
What am I doing wrong?
I believe the props.fetchDatabase() call is asynchronous, so by the time you are attempting to setFilteredTable the props.filtered_table_data has not updated yet.
You can try something like this:
useEffect(() => {
props.fetchDatabase();
props.fetchOptions();
}, [])
useEffect(() => {
setFilteredTable(props.filtered_table_data);
}, [props.filtered_table_data]);
Note that this effect will run every time filtered_table_data changes, so you may need to wrap around the code in the callback with some sort of condition if you want to restrict setting the local state.
useEffect's callback with [] as hook's second argument is only being called once when component just mounted. Inside it fetchDatabase, and fetchOptions callbacks are called, and right after that (when data isn't yet fetched) you call setFilteredTable, that's why there are empty array occurs in filteredTable.
Not sure if this answers your question, but React-Redux provides some hooks for accessing the redux store.
The one that might be of interest to you is the useSelector() hook.
Here's an example usage:
import { useSelector } from 'react-redux';
const App = () => {
const tableData = useSelector(state => state.tableData);
...
}

Unable to setState with array of objects

I'm trying to setState of an empty array with an array of objects on component load.
I've tried new ES6 syntax, I've tried mapping, join, etc but can't get it to work.
The console output of the array I'm trying to insert (not push) into my state looks correct.
mapped arrayObj : [{"word":"teacher","correct":true,"image":"/Assets/Art/Icons/teacher.png"},{"word":"backpack","correct":false,"image":"/Assets/Art/Icons/backpack.png"},{"word":"paper","correct":false,"image":"/Assets/Art/Icons/paper.jpg"}]
Here's the function where I'm mapping my array of objects and then I'm trying to setState of my empty answersObj.
mapArray(){
const arrayObj = this.state.data.answers.map(obj => obj);
let shuffledObjArray = [{}];
shuffledObjArray = this.shuffleArray(arrayObj)
this.setState({
answersObj: shuffledObjArray
})
return shuffledObjArray;
}
I call the mapArray function when the component loads
componentDidMount() {
this.mapArray();
}
Don't forget that setState is async function, so the state doesn't sets immediately.
For example this piece of code works for me:
async componentDidMount() {
await this.mapArray();
console.log(this.state)
}
the obj state gets the value it needs, while w/o the async/await it would print empty state.
Therefore, if you need to render the data from that state I'd suggest making 'dataLoaded' bool and render the data only if the data finished loading.

Resources