Get state from display component - reactjs

I have one fetch and one display .js file. However I am trying to figure out how to read the state. Of course as it's done now it's returned from the other .js file. But I would like to use the state that was set instead. How would I refactor to do this?
I would like to use the stateURL prop in the DataLoader.js
DataLoader.js
import React, { useState, useEffect } from 'react';
import useFetch from "./useFetch";
export default function DataLoader({stateURL}) {
const data = useFetch("/api");
// Should not be used
console.log("data", data);
const data2 = Object.keys(data).map(data => data);
console.log("data2", data2);
const data3 = data2.map(key => {
console.log("inside data3", key );
return data[key];
});
//This is empty
console.log("state", stateURL);
return (
<div>
<h1>Testing</h1>
<ul>
{Object.keys(data3).map(key => {
return <li>{data3[key].href}</li>;
})}
</ul>
</div>
);
}
useFetch.js
import { useState, useEffect } from "react";
export default function useFetch(url) {
const [stateURL, setStateURL] = useState([]);
console.log("url", url);
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => setStateURL(data._links));
}, []);
console.log("stateURL", stateURL);
return stateURL;
}

That is not possible. The hooks can only be referred from the original creating component.
Why do you just use the fetch hook within the display file?
If you want to keep these two components separated:
To access the data, you have to share the data somehow to be accessible to your other components. There are several ways to do it:
Pass the data up into the parent component via a callback and pass that into the other child component.
Using a state management library like Redux or Mobx.
Using the context API to share data between components but that might not be the best way for this kind of data.
It depends on your setup and your needs. If only these two components ever need that data, pushing it into the parent works fine.
If there are other components, which maybe need that data, you might want to use a state management lib.

Related

After useEffect API call, state set by useState for json data being passed to a component as props returns empty array

I'm still a beginner in React and I'm trying to use useEffect to fetch data from an API and then useState to set the state and then pass that state as props to a child component.
But in my child component, it appears as an empty array each time when I do console.log. I understand that on the first render the state of my initial state is an empty array []. But I've been trying to combat this and send the right JSON data but can't seem to do so.
I am trying to do this as I have multiple child components that I wanna send data to.
Below is a workaround I coded up with some digging around but doesn't work either:
const api = 'url string'
const [races, setRaces] = useState([]);
const [races2, setRaces2] = useState([races]);
useEffect(() => {
fetch(api)
.then((resp) => resp.json())
.then((response) => setRaces(response));
}, []);
useEffect(() => {
if (races.length) setRaces2(races);
}, [races]);
<Child data={races2}
But this does not seem work to work either when I do console.log(props.data) in the child component.
This is how normally one would fetch data and try and send the data but in both cases, it's been the same.
const api = 'url string'
const [races, setRaces] = useState([]);
useEffect(() => {
fetch(api)
.then((resp) => resp.json())
.then((response) => setRaces(response));
}, []);
<Child data={races}
Following is a rough flow diagram explaining what I wanna do:
Thank you for your help in advance.
I made this quick example.
Here is what the code does:
Fetching the Data using UseEffect
Storing into State
Passing the State into Component as Props
Fetching the Props and Displaying the data.
Code for App.js
import "./styles.css";
import ChildComponent from "./ChildComponent";
import { useEffect, useState } from "react";
export default function App() {
const [title, setTitle] = useState(null);
// * Init on Page Load
useEffect(() => {
fetchTitle();
}, []);
const fetchTitle = async () => {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts/1"
);
const data = await response.json();
setTitle(data.title); //Setting the response into state
};
return (
<div className="App">
<ChildComponent data={title} />
</div>
);
}
Code for ChildComponent.js
export default function ChildComponent({ data }) {
return <div>{data}</div>;
}
I created this Codesandbox. This might help.
https://codesandbox.io/s/elegant-lumiere-cg66nt
Array and object are referential data types, passing as array dependency will not re-run side effect. useEffect dependencies should be primitive data type (string, number, boolean,undefined or null).
useEffect(() => {
if (races.length) setRaces2(races);
}, [races.length])// Dependencies must be primitive data type not referencial.

How to properly passing data through functional component in ReactJS?

I am new to react and this is very confusing to me. Any help would be appreciated.
So I have an Axios Interceptor, making sure the user is authenticated, but that not the issue, the issue is the ".then()" part of the interceptor. So I am trying to pass "res" into my functional component "Profile" like below.
export function GetProfiles(history) {
axiosInstance(history)
.get('/profile')
.then((res) => <Profile userData={UserProfile(res)} />)
.catch((err) => console.log("err", err));
}
So this is how to write my "UserProfile(res)" function
function UserProfile(props) {
let data = {
firstName: props.data.firstName,
lastName: props.data.lastName,
email: props.data.email,
phone: props.data.phone,
};
return { data };
}
export default UserProfile;
If I do console.log(data) in "UserProfile" I get all the data I needed. So everything is working as intended. However, when I try to retrieve those same data in the "Profile" component I get "undefined". So this is how I write my "Profile" component
function Profile({ userData }) {
console.log(userData);
}
export default Profile;
Again, any help would very much appreciate, I am new to this so there is a very big chance I am doing it wrong. Please point me in the right direction.
When you are fetching data from an API, normally you'd assign the response (res) to a variable, that way you separate the View (The component structure) from the Data (The user info from the API). So, in your case, you'd have to:
Define a variable to store the user data.
After that, inside the getProfile function, assign the response to the variable.
And finally, pass the variable as a prop to your component.
You can use this code as an example:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function App() {
const [profileData, setProfileData] = useState();
useEffect(() => {
const fetchData = async () => {
const result = await axios(
'yourapiurl/profile',
);
setProfileData(result.data);
};
fetchData();
}, []);
return (
<>
<Profile userData={profileData} />
</>
);
}
export default App;
In this example, I'm using React Hooks, so you do your API call inside the useEffect hook. I defined a variable called profileData, where I will store the data from the API. Inside the fetchData function, I call the method setProfileData, so all the data that you got from the API will be stored inside the profileData variable. Finally, you pass the profileData as a prop to your Profile Component, and it will update as soon as the data is fetched from your API.
I got all the information from this link:
https://www.robinwieruch.de/react-hooks-fetch-data
In case you are using Class Components and not React Hooks, the process is very similar, just instead of defining the variable like this:
const [profileData, setProfileData] = useState();
You'd have to define it as the state of your component:
state = {
profileData: []
}
More info about how to fetch data from an API using Axios in React Class Components in the following link:
https://www.digitalocean.com/community/tutorials/react-axios-react
I hope this info was useful.
Happy Hacking!
I think you're trying to write UserProfile as a helper function but instead it looks like a function component the way you have it. You could map data in the .then chain before passing it down to your Profile component i.e.
let userData = userProfile(res);
return <Profile userData={userData} />

React useEffect inside const function with MobX

I have some React Redux code written in Typescript that loads some data from my server when a component mounts. That code looks like this:
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { MyAction } from 'my/types/MyAction';
export const useDispatchOnMount = (action: MyAction) => {
const dispatch = useDispatch();
return useEffect(() => {
dispatch(action);
}, [dispatch]);
};
This is simple enough - it uses the useEffect hook to do what I want. Now I need to convert the code so that it uses MobX instead of Redux for persistent state. If I have my own MobX store object called myStore, and myStore has a async method "loadXYZ" that loads a specific set of data XYZ, I know I can do this inside my component:
useEffect(() => {
async functon doLoadXYZ() {
await myStore.loadXYZ();
}
doLoadXYZ();
}, []);
This does indeed work, but I would like to put all this into a single fat arrow function that calls useEffect, much like what the useDispatchOnMount function does. I can't figure out the best way to do this. Anyone know how to do this?
EDIT: After further digging, it looks more and more like what I am trying to do with the Mobx version would break the rules of Hooks, ie always call useEffect from the top level of the functional component. So calling it explicitly like this:
export const MyContainer: React.FC = () => {
useEffect(() => {
async functon doLoadXYZ() {
await myStore.loadXYZ();
}
doLoadXYZ();
}, []);
...
};
is apparently the best way to go. Butthat raises the question: is the redux version that uses useDispatchOnMount a bad idea? Why?
You can do this if you don't use async/await in the useEffect. If you are fetching data, I would store it in myStore and use it directly out of the store instead of using async/await. It might look something like this:
export const SomeComp = observer(function SomeComp() {
const myStore = useStore() // get the store with a hook or how you are getting it
useEffect(myStore.loadXYZ, [myStore])
return <div>{myStore.theLoadedData}</div>
})
In loadXYZ you just store the data the way you want and use it. The component observing theLoadedData will re-render when it comes back so you don't need to have async/await in the component.

React: Stop hook from being called every re-rendering?

Somewhat new to React and hooks in React. I have a component that calls a communications hook inside of which a call to an API is made with AXIOS and then the JSON response is fed back to the component. The issue I'm having is the component is calling the hook like six times in a row, four of which of course come back with undefined data and then another two times which returns the expected JSON (the same both of those two times).
I did a quick console.log to double check if it was indeed the component calling the hook mulitple times or it was happening inside the hook, and it is the component.
How do I go about only have the hook called only once on demand and not multiple times like it is? Here's the part in question (not including the rest of the code in the widget because it doesn't pertain):
export default function TestWidget() {
//Fetch data from communicator
console.log("called");
const getJSONData = useCommunicatorAPI('https://jsonplaceholder.typicode.com/todos/1');
//Breakdown passed data
const {lastName, alertList, warningList} = getJSONData;
return (
<h1 id="welcomeTitle">Welcome {lastName}!</h1>
);
}
export const useCommunicatorAPI = (requestAPI, requestData) => {
const [{ data, loading, error }, refetch] = useAxios('https://jsonplaceholder.typicode.com/todos/1', []);
console.log("data in Communicator:", data);
return {data};
}
I would use the useEffect hook to do this on mount and whenever any dependencies of the request change (like if the url changed).
Here is what you will want to look at for useEffect
Here is what it might look like:
const [jsonData, setJsonData] = React.useState({})
const url = ...whatver the url is
React.useEffect(() => {
const doFetch = async () => {
const jsonData = await useAxios(url, []);;
setJsonData(jsonData)
}
doFetch();
}, [url])
...use jsonData from the useState
With the above example, the fetch will happen on mount and if the url changes.
Why not just use the hook directly?
export default function TestWidget() {
const [{ data, loading, error }, refetch] =
useAxios('https://jsonplaceholder.typicode.com/todos/1', []);
return (<h1 id="welcomeTitle">Welcome {lastName}!</h1>);
}
the empty array [] makes the hook fire once when called
Try creating a function with async/await where you fetch the data.
Here can you learn about it:
https://javascript.info/async-await

React get state from Redux store within useEffect

What is the correct way to get state from the Redux store within the useEffect hook?
useEffect(() => {
const user = useSelector(state => state.user);
});
I am attempting to get the current state within useEffect but I cannot use the useSelector call because this results in an error stating:
Invariant Violation: Hooks can only be called inside the body of a function component.
I think I understand why as it breaks one of the primary rules of hooks.
From reviewing the example on the Redux docs they seem to use a selectors.js file to gather the current state but this reference the mapStateToProps which I understood was no longer necessary.
Do I need to create some kind of "getter" function which should be called within the useEffect hook?
Don't forget to add user as a dependency to useEffect otherwise your effect won't get updated value.
const user = useSelector(state => state.user);
useEffect(() => {
// do stuff
}, [user]);
You can place useSelector at the top of your component along with the other hooks:
const MyComponent = () => {
...
const user = useSelector(state => state.user);
...
}
Then you can access user inside your useEffects.
I found using two useEffects to works for me, and have useState to update the user (or in this case, currUser).
const user = useSelector(state=>state.user);
const [currUser, setCurrUser] = useState(user);
useEffect(()=>{
dispatch(loadUser());
}, [dispatch]);
useEffect(()=>{
setCurrUser(user);
}, [user]);
You have to use currUser to display and manipulate that object.
You have two choices.
1 - If you only need the value from store once or 'n' time your useEffect is called and don't want to listen for any changes that may occur to user state from redux then use this approach
//import the main store file from where you've used createStore()
import {store} from './store' // this will give you access to redux store
export default function MyComponent(){
useEffect(() =>{
const user = store.getState().user;
//...
},[])
}
2 - If you want to listen to the changes that may occur to user state then the recommended answer is the way to go about
const MyComponent = () => {
//...
const user = useSelector(state => state.user);
useEffect(() => {
//...
},[])
//...
}
const tournamentinfofromstore=useSelector(state=>state.tournamentinfo)
useEffect(() => {
console.log(tournamentinfofromstore)
}, [tournamentinfofromstore])
So the problem is that if you change the state inside the useEffect that causes a rerender and then again the useEffect gets called "&&" if that component is passing data to another component will result in infinite loops.and because you are also storing that data in the child component's state will result in rerendering and the result will be infinite loop.!!
Although it is not recommended, you can use store directly in your component, even in the useEffect.
First, you have to export store from where it is created.
import invoiceReducer from './slices/invoiceSlice';
import authReducer from './slices/authSlice';
export const store = configureStore({
reducer: {
invoices: invoicesReducer,
auth: authReducer,
},
});
Then you can import it to a React Component, or even to a function, and use it.
import React, { useEffect } from 'react';
import { store } from './store';
const MyComponent = () => {
useEffect(()=> {
const invoiceList = store.getState().invoices
console.log(invoiceList)
}, [])
return (
<div>
<h1>Hello World!</h1>
</div>
)
}
export default MyComponent
You can study the API for Store in here.
You can also see why this approach is not recommended in
here.
Or, if you are interested in using redux store outside a react component, take a look at this blog post.
To add on top of #Motomoto's reply. Sometimes you depend on store to be loaded before useEffect. In this case you can simply return in if the state is undefined. useEffect will rerender once the store is loaded
const user = useSelector(state => state.user);
useEffect(() => {
if(user === undefined){
return}else{
// do stuff
}}, [user]);
I'm having the same issue, The problem to the useSelector is that we cant call it into the hook, so I can't be able to update with the action properly. so I used the useSelector variable as a dependency to the useEffect and it solved my problem.
const finalImgData_to_be_assigned = useSelector((state) => state.userSelectedImg);
useEffect(()=>{
console.log('final data to be ready to assign tags : ', finalImgData_to_be_assigned.data);
}, [finalImgData_to_be_assigned ])

Resources