What's wrong with the following component?
Its throws an error while the original class based version with life cycle methods works fine?
import React, { useState, useEffect } from "react";
import axios from "axios";
const NewsHook = ()=> {
const [mount, setMount] = useState(false);
const [news, setNews] = useState([]);
useEffect(() => {
setMount(true);
axios.get('https://hn.algolia.com/api/v1/search?query=react')
.then(result => {
if (mount){
setNews({
news: result.data.hits,
})
}
}
);
return () => {
setMount(false);
}
}, [mount]);
return (
<ul>
{news.map(topic => (
<li key={topic.objectID}>{topic.title}</li>
))}
</ul>
);
}
export default NewsHook
the solution was that the following:
setNews(result.data.hits);
The problem is because the data is fetching. So news will be still null. Either using async to solve the problem or doing in this way :)
import React, { useEffect, useState } from "react";
import axios from "axios";
import "./styles.css";
export default function App() {
const [mount, setMount] = useState(false);
const [news, setNews] = useState([{ new: null }]);
useEffect(() => {
setMount(true);
axios
.get("https://hn.algolia.com/api/v1/search?query=react")
.then(result => {
setNews({ new: result.data.hits });
});
return () => {
setMount(false);
};
}, [mount]);
return (
<div>
<ul>
{news.new == null ? (
<h2>Loading</h2>
) : (
news.new.map(item => {
return <li key={item.objectID}>{item.title}</li>;
})
)}
</ul>
</div>
);
}
it will solve your problem.
Hope it is help :)
you don't need to set a state of mount because the useEffect will work automatically when the component mounts and if you want it to work just ones, you add empty array [] as the second argument
and you have to wrap your Axios request inside useEffect to an async function like that:
import React, { useState, useEffect } from "react";
import axios from "axios";
const NewsHook = ()=> {
const [news, setNews] = useState([]);
useEffect(() => {
const getNews = async () => {
const result = await axios.get('https://hn.algolia.com/api/v1/search?query=react');
setNews({
news: result.data.hits,
})
};
getNews();
}, []);
return (
<ul>
{news.map(topic => (
<li key={topic.objectID}>{topic.title}</li>
))}
</ul>
);
};
export default NewsHook
Related
I can't render props from useContext because it returns undefined before desired object. I can't seem to find any solution to my problem.
This is the child element I'm trying to render:
const Reviews = ({reviews}) => {
return (
<div className={styles['review__card']}>
{reviews.map((review) => {
return(
<div className={styles['review__card__item']}>
<div className={styles['card__item__meta']}>
<span>{review.name}</span>
<span><StarRating rating={review.rating}/></span>
</div>
<div className={styles['card__item__p']}>
<p>{review.revew}</p>
</div>
</div>
)})}
</div>
)
}
export default Reviews
This is Parent Element:
import React, { useContext, useEffect } from 'react'
import { useParams } from 'react-router-dom'
import { RestaurantsContext } from '../context/RestaurantsContext';
import Wrapper from '../components/Wrapper'
import Header from '../components/Header'
import Reviews from '../components/Reviews'
import AddReview from '../components/AddReview'
import RestaurantFinder from '../apis/RestaurantFinder';
const RestaurantDetailPage = () => {
const {id} = useParams()
const {selectedRestaurant, setSelectedRestaurant} = useContext(RestaurantsContext)
useEffect(()=> {
const fetchDate = async () => {
try {
const response = await RestaurantFinder.get(`/${id}`)
setSelectedRestaurant(response.data.data)
}
catch (err) {console.log(err)
}
}
fetchDate()
}, [])
console.log(selectedRestaurant.reviews)
return (
<Wrapper>
<Header title={ 'RestaurantDetailPage' }/>
<div>{selectedRestaurant && (
<>
<Reviews reviews={selectedRestaurant.reviews}/>
<AddReview/>
</>
)}</div>
</Wrapper>
)
}
export default RestaurantDetailPage
Whenever I console.log(selectedRestaurant.reviews) it gives me undefined and then it gives me object query. I assume that .map() is getting an error because it is trying to render that first argument which is undefined
Here is Context js
import React, {useState, createContext} from 'react';
export const RestaurantsContext = createContext();
export const RestaurantsContextProvider = props => {
const [restaurants, setRestaurants] = useState([])
const [selectedRestaurant, setSelectedRestaurant] = useState([])
const addRestaurants = (restaurant) => {
setRestaurants([...restaurants, restaurant]);
}
return (
<RestaurantsContext.Provider value={{restaurants, setRestaurants, addRestaurants, selectedRestaurant, setSelectedRestaurant }}>
{props.children}
</RestaurantsContext.Provider>
)
}
I have found the issue and solved it though I'm not entirely sure how it works.
const [selectedRestaurant, setSelectedRestaurant] = useState([])
default useState value should be null using useState(null)
Quick Help Needed! I have Two React components Vendors and VendorsList. In Vendors.js Component i have asset.asset_name text rendered in table format. What I want is, When I click on I asset.asset_name, I wanted to pass it's value from Vendors component to VendorsList component. How could I do this?
Here is code for Two Components
Vendors.js
import React, { useEffect, useState } from "react";
import { axios } from "axios";
const Vendors = () => {
const [data, setData] = useState({});
const baseURL =
"http://127.0.0.1:8000/api/business_process/business-impact/business-assets-detail";
useEffect(() => {
axios
.get(baseURL)
.then((response) => {
setData(response.data);
})
.then(
(response) => {},
(err) => {
alert("No Data To Show");
}
)
.catch((err) => {
return false;
});
}, []);
const DisplayData = data.business_assets?.map((asset) => {
return (
<tr>
<td>{asset.business_assets}</td>
<td onClick={() => alert(asset.asset_name)}>{asset.asset_name}</td>
</tr>
);
});
return <div></div>;
};
export default Vendors;
Here is VendorsList.js
import React from "react";
const VendorsList = () => {
return (
<div>
<h1>{foo}</h1>
</div>
);
};
export default VendorsList;
I need asset.asset_name value to be passed to VendorsList when I click on asset.asset_name value from Vendors component
I don't think you're passing the asset_name into your VendorsList component at all. I think the quickest way is to directly render the VendorsList in your Vendors component by putting it in the return of your Vendors component. You would also need something to record what you have clicked so you can use another useState for this. Below is how you'd achieve this:
Modify your Vendor.js to look like this:
import React, { useEffect, useState } from "react";
import { axios } from "axios";
import VendorsList from '<path to your VendorList component>'
const Vendors = () => {
const [data, setData] = useState({});
const [clickedAsset, setClickedAsset] = useState()
const baseURL =
"http://127.0.0.1:8000/api/business_process/business-impact/business-assets-detail";
useEffect(() => {
axios
.get(baseURL)
.then((response) => {
setData(response.data);
})
.then(
(response) => {},
(err) => {
alert("No Data To Show");
}
)
.catch((err) => {
return false;
});
}, []);
const DisplayData = data.business_assets?.map((asset) => {
return (
<tr>
<td>{asset.business_assets}</td>
<td onClick={() => setClickedAsset(asset.asset_name)}>{asset.asset_name}</td>
</tr>
);
});
return (
<div>
<DisplayData/>
<VendorList clickedAssetName={clickedAsset}/>
</div>
);
};
export default Vendors;
Then to use the clickedAssetName that you just passed, access it like this in your VendorsList.js component:
import React from "react";
const VendorsList = ({clickedAssetName}) => {
return (
<div>
<h1>{clickedAssetName}</h1>
</div>
);
};
export default VendorsList;
I'm trying to get the id from the URL, so I can get a specific post from the API, in order to return it. I can't use Class Component to do so, because useParams only works with functions. I try to put the data inside the variable post, but it also didn't work.
import { useParams } from "react-router-dom";
import axios from "axios";
const Post = () => {
let params = useParams();
let post = null;
axios.get('https://jsonplaceholder.typicode.com/posts/' + params.post_id)
.then(res => {
post = res.data ? (
<div>
<h4>{res.data.title}</h4>
<p>{res.data.body}</p>
</div>
) : (
<div>Loading post...</div>
);
});
return (
<div>{post}</div>
)
}
export default Post
Dynamic data, like a request with axios should be done within a useEffect hook. With the dependencies array empty [] it provides that the request in the useEffect hook will only happen the first render, but not the following after.
When the response comes in, save the result in a state. Render the proper data based on the value of the state.
import { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import axios from "axios";
const Post = () => {
let params = useParams();
const [post, setPost] = useState(null)
useEffect(() => {
axios.get('https://jsonplaceholder.typicode.com/posts/' + params.post_id)
.then(post => {
setPost(post)
});
}, [])
return (
<div>
{post !== null ? (
<div>
<h4>{res.data.title}</h4>
<p>{res.data.body}</p>
</div>
) : (
<div>Loading post...</div>
)}
</div>
)
}
export default Post
You should use useEffect and useState hooks in this way
import { useEffect, useState } from "react";
import axios from "axios";
import { useParams } from "react-router-dom";
import "./App.css";
function App() {
let params = useParams();
const [postState, setPostState] = useState({ data: {}, loading: false });
useEffect(() => {
axios.get("https://jsonplaceholder.typicode.com/posts/"+ params.post_id).then((res) => {
if (res.data) {
setPostState((s) => ({ ...s, data: res.data }));
} else {
setPostState((s) => ({ ...s, loading: true }));
}
});
}, []);
const { data, loading } = postState;
return (
<>
{data && !loading ? (
<div>
<h4>{data.title}</h4>
<p>{data.body}</p>
</div>
) : (
<div>Loading post...</div>
)}
</>
);
}
export default App;
Goal: Fetch data from api then assign it to a state for further processing.
Issue: After setting the data to my useState it is still undefined.
Questions:
How would one solve this problem?
Am I misunderstanding the useState hook?
import "./styles.css";
import axios from "axios";
import { useEffect, useState } from "react";
export default function App() {
const [userData, setUserData] = useState();
const functionz = () => {
return axios
.get("https://randomuser.me/api/")
.then(({ data }) => data.results);
};
useEffect(async () => {
const data = await functionz();
setUserData(data);
}, []);
if (userData) {
console.log(userData);
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Edit to see some magic happen!</h2>
</div>
);
}
You have to make sure your function is returning the axios call. Then await whatever comes out of it in your useEffect. Then proceed to adding it to your state. See example.
import React, { useState, useEffect } from 'react'
import axios from "axios";
const Api = () => {
const [usersData, setUsersData] = useState(null)
const fetchRandomUserData = () => axios.get('the-url')
useEffect(() => {
fetchRandomUserData()
.then(resp => {
setUsersData(resp.data.results)
})
.catch(e => {
console.log('Error: ', e)
})
}, [])
console.log(usersData)
return <div></div>
}
export default Api
I have tried some solutions that came by, on this link particularily...
I tried changing value inside my TodosContext.js file.. which didn't work, too..
One more thing that I have tried is to call useContext() function from another component, that didn't work neither..
Here's my code.
App.js:
import React, { useState, useContext } from 'react';
import TodoList from './components/TodoList';
import NewTodo from './components/NewTodo';
import { TodosProvider, TodosContext } from './components/contextapi/TodosContext';
function App() {
const [input, setInput] = useState('');
const [todos, setTodos] = useContext(TodosContext);
const _handleInput = (e) => {
setInput(e.target.value)
}
const _todoAdd = (e) => {
if (e.key === 'Enter') {
setTodos(
[...todos, { content: input, id: Date.now(), completed: false }]
)
setInput('')
}
}
const _todoRemove = (id) => {
const newTodos = todos.filter(todo => todo.id !== id)
setTodos(newTodos)
}
return (
<div>
<header>
<h3>To-Do Manager | Context API</h3>
</header>
<TodosProvider>
<NewTodo newTodo={_todoAdd} handleInput={_handleInput} newVal={input} />
<TodoList todos={todos} />
</TodosProvider>
</div>
);
}
export default App;
TodosContext.js:
import React, { useState, createContext } from 'react';
export const TodosContext = createContext()
export const TodosProvider = ({ children }) => {
const [todos, setTodos] = useState([]);
return (
<TodosContext.Provider value={[todos, setTodos]}>{children}</TodosContext.Provider>
)
}
TodoList.js:
import React, { useContext } from 'react';
import Todo from './Todo';
import RemoveTodoFromList from './RemoveTodoFromList';
import { TodosContext } from './contextapi/TodosContext'
function TodoList() {
const [todos, setTodos] = useContext(TodosContext);
return (
<div>
{todos.map(todo => (
<div>
<Todo key={todo.id} todo={todo} />
</div>
))}
</div>
)
}
export default TodoList
I'm really struggling with this one, I spent whole day figuring out what went wrong.. Thanks!
We fixed it inside the comments.
createContext needs an object as parameter that defines your context.
In your case it should be export const TodosContext = createContext([[],() => {}]).
So the application knows the first element of the tuple is an array and so iterable.