I want to direct User Profile when a username is clicked. When click the username, get the data of users data is taken and define to userProfile with setUserProfile. After that, get the data of user posts. Then direct to the /users/${userName}. Everything works correctly until coming to the Map function inside UserPost.js but the inside of the userPosts.map() function is not rendered.
After Click the username the API calls start using the username:
useEffect(() => {
async function fetchProfileAPI() {
if (userName == undefined) {
return null;
}
const urlUserbyName = `http://localhost:5000/users/${userName}`;
const result = await axios(urlUserbyName);
setUserProfile(result.data);
}
fetchProfileAPI();
}, [userName]);
useEffect( () => {
async function fetchUserPostsAPI() {
if (userProfile == undefined) {
return null;
}
const postsOfUser = userProfile.createdPost;
const postArray = [];
postsOfUser.map(async (postId) => {
const urlPostbyId = `http://localhost:5000/posts/${postId}`;
const result = await axios(urlPostbyId);
postArray.push(result.data);
})
console.log(postArray);
setUserPosts(postArray);
}
fetchUserPostsAPI();
},[userProfile]);
// History.Push in useEffect
useEffect(() => {
if(userProfile == undefined) {
console.log("Nullmuş ya la");
return null;
}
console.log("Null değilmiş ya la");
history.push(`/users/${userProfile.username}`);
},[userPosts]);
App.js React Router Part
const App = () => {
const { postId, userName } = useContext(MemoryContext);
return (
<>
<Router>
<Switch>
<Route path={`/users/${userName}`} exact>
<Header/>
<Profile/>
<UserPosts/>
</Route>
<Route path={`/posts/${postId}`} exact>
<Header />
<SinglePost/>
</Route>
<Route path="/posts" exact>
<Header />
<PostsList />
<AddPost />
</Route>
<Route path="/" exact>
<Header />
<PostsList />
<AddPost />
</Route>
</Switch>
</Router>
</>
)
}
UserPost.js
Everything is fine until here.
const UserPost = () => {
const { userPosts } = useContext(MemoryContext);
console.log(userPosts); // This line is printed to console and shows the api call is working correctly
return (
<Container>
<Row>
{console.log("UserPosts Rendered...")} {/*This line is printed to console*/}
{userPosts.map((post, key) => {
return (
<Col key={key} xs={10} md={6} lg={4} className="offset-1 offset-md-0 py-5 px-3">
{console.log("UsersPost.map working") /*This line is not printed to console*/}
</Col>
)
})}
</Row>
</Container>
)
}
Related
I'm trying to maintain state values between routes in context. But it gets reset when the route changes.
aackage.json:
"react-router-dom": "^6.8.0",
"react-dom": "^18.2.0",
"react": "^18.2.0",
App.js:
export default const App = () => {
const [loading, setLoading] = useState(false);
const [data, setData] = useState(null);
const getData = async () => {
setLoading(true);
const data = await axios.get("url", {
withCredentials: true,
});
setData(data);
setLoading(false);
};
useEffect(() => {
getData()
console.log("I run on route change");
}, []);
const GlobalContextValue= {
data: data,
loading: loading,
};
return (
<>
<GlobalContextProvider value={GlobalContextValue}>
<BrowserRouter>
<Routes>
<Route index element={<HomePage />} />
<Route path="/:slug" element={<PostPage />} />
{/* <Route path="*" element={<NoPage />} /> */}
</Routes>
</BrowserRouter>
</<GlobalContextProvider />
</>
)
}
Whenever I try to access any route the getData function inside the useEffect calls which inturns resets the data. I have attached a CodeSandbox to replicate the same
I don't know if this problem is related to reactJs or react-router. Thanks in advance
As you don't seem to have any navigation link, I assume you are using the browser search bar, or a normal HTML <a> tag. Well, doing so refreshes the page, so the entire app gets re-created.
Using useNavigate or Link from React Router Dom, doesn't refresh the page, hence your context data remains untouched:
const HomePage = () => {
return (
<>
<h1>Hii Homepage </h1>
<Link to="/1">Go to PostPage</Link>
</>
);
};
const PostPage = () => {
const params = useParams();
return (
<>
<h1>Hii PostPage {params.slug} </h1>
<Link to="/">Go to HomePage</Link>
</>
);
};
export default function App() {
useEffect(() => {
console.log(
"I run on load and route change with browser search bar, not with useNavigate or Link"
);
}, []);
return (
<>
{/* This context wrapping BrowserRouter keeps its value if you navigate with Link or
useNavigate. */}
<GlobalContextProvider value={{ key: "some value" }}>
<BrowserRouter>
<Routes>
<Route index element={<HomePage />} />
<Route path="/:slug" element={<PostPage />} />
{/* <Route path="*" element={<NoPage />} /> */}
</Routes>
</BrowserRouter>
</GlobalContextProvider>
</>
);
}
Looking to solve how to pass my search results to other components so when users use the search bar, the searched results gets displayed instead of that components rendered data.. in this case it would homeScreen. using react router v5 and i tried passing it through the router but many attempts didn't work, should i create a seperate search router too?
App.js:
<Container>
<Route path="/" component={HomeScreen} exact />
<Route path="/login" component={LoginScreen} exact />
<Route path="/register" component={RegisterScreen} exact />
<Route path="/product/:id" component={ProductScreen} exact />
<Route path="/cart/:id?" component={CartScreen} exact />
</Container>
header.js:
function Header() {
const userLogin = useSelector((state) => state.userLogin);
const { userInfo } = userLogin;
// const [items, setItems] = useState("");
const [searchResults, setSearchResults] = useState([]);
const debounce = useDebounce(searchResults, 500);
const dispatch = useDispatch();
const logoutHandler = () => {
dispatch(logout());
};
useEffect(() => {
axios.get(`/api/search/?search=${searchResults}`).then((response) => {
setSearchResults(response.data[0]);
console.log(response.data[0]);
});
}, [debounce]);
const handleSearch = (e) => {
setSearchResults(e.target.value);
};
return (
<div>
<Navbar bg="dark" variant="dark" className="navCustom">
<Container>
<LinkContainer to="/">
<Navbar.Brand>eCommerce</Navbar.Brand>
</LinkContainer>
<Form className="d-flex">
<Form.Control
type="search"
placeholder="Search"
className="me-2"
aria-label="Search"
onChange={handleSearch}
/>
<Button variant="outline-success">Search</Button>
</Form>
HomeScreen.js:
function HomeScreen({ searchResults }) {
const dispatch = useDispatch();
const productList = useSelector((state) => state.productList);
const { error, loading, products } = productList;
useEffect(() => {
dispatch(listProducts());
}, [dispatch]);
return (
<div>
{searchResults.length > 0 ? (
<Row>
{searchResults.map((product) => (
<Col key={product._id} sm={12} md={6} lg={4} xl={3}>
<Product product={product} />
</Col>
))}
</Row>
) : (
// Fall back to rendering regular products
<Row>
{products &&
products.map((product) => (
<Col key={product._id} sm={12} md={6} lg={4} xl={3}>
<Product product={product} />
</Col>
))}
</Row>
)}
</div>
);
}
export default HomeScreen;
This seems like a good use case of React Context. Within the Context, you can use useState to set the results. The Context can be provided to other components within your app.
So what I mean is, I have a functionality of after user signed in, navigate him/her to the homepage in a second, such as:
const handleSignIn = async (e) => {
e.preventDefault();
const user = await signIn(formData);
if (user) {
toast.success('Signed In!');
setTimeout(() => {
navigate('/');
}, 1000);
} else {
toast.error('Bad user credentials!');
}
};
But, I built a protected route, such as:
function App() {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setIsLoading(true);
if (user) {
setUser(user);
} else {
setUser(null);
}
setIsLoading(false);
});
return unsubscribe;
}, []);
if (isLoading) return <Spinner />;
return (
<>
<Router>
<Routes>
<Route path='/' element={<PrivateRoute />}>
<Route index element={<Explore />} />
<Route path='/offers' element={<Offers />} />
<Route path='/profile' element={<Profile />} />
<Route path='/contact/:landlordId' element={<Contact />} />
<Route path='/create-listing' element={<CreateListing />} />
<Route path='/category/:categoryName' element={<Category />} />
<Route path='/category/:categoryName/:id' element={<Listing />} />
</Route>
<Route
path='/sign-in'
element={!user ? <SignIn /> : <Navigate to='/' />}
/>
<Route
path='/sign-up'
element={!user ? <SignUp /> : <Navigate to='/' />}
/>
<Route
path='/forgot-password'
element={!user ? <ForgotPassword /> : <Navigate to='/' />}
/>
</Routes>
{user && <Navbar />}
</Router>
<ToastContainer autoClose={1000} />
</>
);
}
export default App;
So the problem here is, whenever user signs in or signs out,the onAuthStateChanged gets executed, therefore it enforce app to re-render in order to understand who's logged in and logged out then behave accordingly, like restrict some routes and allow others.
When user signs in, I'd like to show some toast message BUT it triggers the App level state and before I show toast and navigate user, it automatically navigates user into the homepage due to the restriction, how can I wait a second before doing that?
Can't you just wait for x amount of time before calling setUser()? That is because setUser() updates the user value, which triggers navigation.
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setIsLoading(true);
setTimeout(() => {
if (user) {
setUser(user);
} else {
setUser(null);
}
setIsLoading(false);
}, 1000);
});
return unsubscribe;
}, []);
Alternatively, use async/await with a custom wait async function that will avoid nesting:
const wait = async (d) => new Promise(r => setTimeout(d, r));
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, async (user) => {
setIsLoading(true);
await wait(1000);
if (user) {
setUser(user);
} else {
setUser(null);
}
setIsLoading(false);
});
return unsubscribe;
}, []);
Allow the SignIn, component to handle the redirect itself. You have two different pieces of logic that are redirecting to "/"
Instead of this:
<Route
path='/sign-in'
element={!user ? <SignIn /> : <Navigate to='/' />}
/>
Switch to this:
<Route
path='/sign-in'
element={<SignIn />}
/>
If that causes a problem (ie, if you want to bypass SignIn and go right to / if the user is already logged in), you should add a useEffect call to SignIn and call navigate('/'); there if necessary.
I am a newbie trying to build a React blog. Routes etc work fine except when a dynamic route comes into question. The Link does not render unless refreshed.
Here is App.js where the Routes are defined
function App() {
const [username, setUsername] = useState('');
const [loggedIn, setLoggedIn] = useState(false);
return (
<>
<Router history={customHistory}>
<div className="App">
<UserContext.Provider value={{ username, setUsername, loggedIn, setLoggedIn }}>
<Navigation />
<Switch>
<Route path="/login" component={Login} />
<Route path="/register" component={Register} />
<Route exact path="/blog/:id" component={BlogDetails} />
<Route path="/content" component={Content} />
<Route path="/logout" component={Logout} />
<Route exact path="/" component={Home} />
</Switch>
</UserContext.Provider>
</div>
</Router>
</>
)
}
export default App;
Here is BlogItems.js which calls the :
import { Link } from 'react-router-dom';
function BlogItems() {
const [blogList, setBlogList] = useState([])
useEffect(() => {
Axios.get("http://localhost:3001/api/get"
, { withCredentials: true })
.then((res) => {
setBlogList(res.data);
});
}, []);
return (
<div className="blog-list">
{blogList && blogList.map((blog) => {
return (
<div className="blog-elems" key={blog && blog.id}>
<Link to={`/blog/${blog.id}`}>{blog.title}</Link>
<h6>By <span>Ritu Rawat</span> on {DateFormater(blog.dated)}</h6>
<div className="blog-list-body">{blog && blog.body.slice(0, 200) + '...'}</div>
</div>
);
})}
</div>
) }
export default BlogItems;
and this is the actual BLogDetails Page which needs to be called from the dynamic route:
const BlogDetails = (props) => {
console.log(props);
//const { username } = useContext(UserContext);
const [blog, setBlog] = useState(null);
const { id } = useParams();
useEffect(() => {
console.log("BLOG ARTICLE");
Axios.get(`http://localhost:3001/api/blog/${id}`
, { withCredentials: true })
.then((res) => {
setBlog(res.data[0]);
});
}, [id]);
return (
<>
<div className="blog-background">
<div className="blog-list">
<div className="blog-elems" key={blog && blog.id}>
<h2> {blog && blog.title}</h2>
<h6>By <span>Ritu Rawat</span> on {blog && DateFormater(blog.dated)}</h6>
<div className="blog-list-body">{blog && blog.body}</div>
</div>
<Comments id={id} />
</div>
</div>
</>
)
}
export default BlogDetails;
I have a parent app which contains a handler function (handleChallengeSave).
The handler function triggers a useState (setSavedChallenge) in the parent.
The handler function is passed down as props to the child.
I'm getting an 'Unhandled Rejection (TypeError): X is not a function' error. However if I change nothing other than moving the state to the child, it works.
Eg:
This doesn't work:
Parent (App.js)
const App = () => {
const [savedChallenge, setSavedChallenge] = useState("");
const handleChallengeSave = (challenge) => {
setSavedChallenge(challenge);
};
return (
<>
<Router>
<Route
path="/"
exact
component={Home}
handleChallengeSave={handleChallengeSave}
/>
</Router>
</>
);
};
The Child (Home.js)
const Home = ({ handleChallengeSave }) => {
const getRequestUserChallengeDb = async () => {
await axios
.get(`${USER_CHALLENGE_DB_LINK}/${STRAVA_ID}`)
.then((res) => {
if (res.status === 200) {
console.log("Yes keen bean! You're in a challenge.");
let yourCurrentChallenge = res.data.currentChallenge;
handleChallengeSave(yourCurrentChallenge);
}
if (res.status === 201) {
console.log(
"You ain't in a challenge mate. Head to the challenges page to join one!"
);
}
})
.catch((error) => {
throw error;
});
};
getRequestUserChallengeDb();
return (
<>
<Navbar />
<div className="homepage_container">
<h2>Hi {window.localStorage.firstName}</h2>
</div>
<Challengebutton />
</>
);
};
export default Home;
Any help MUCH appreciated!
Issue
The Route component doesn't pass additional props on to children.
Solution
Render Home on the render prop to pass in additional props.
<Router>
<Route
path="/"
exact
render={(routeProps) => (
<Home
{...routeProps}
handleChallengeSave={handleChallengeSave}
/>
)}
/>
</Router>
Or render Home as a child component.
<Router>
<Route
path="/"
exact
>
<Home
{...routeProps}
handleChallengeSave={handleChallengeSave}
/>
</Route>
</Router>