Not dispatching an action on Routing in react - reactjs

I have a component called OneUser , i have input field and button , on clicking that button i will fetch the new title from API and route to new page ,title will be shown in that component called Display.
What is happening now is , on clicking of submit button , routing is happening , but API call is not working .
Please suggest the appropriate answer for me
State logic is working fine , so i didnt posted here.
App.js
import React from 'react';
import './App.css';
import OneUser from './Components/OneUser';
import Display from './Components/Display';
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import store from './store'
import {Provider} from 'react-redux'
function App() {
const isAuthenticated = true
return (
<Provider store={store}>
<Router>
<div className="App">
<Switch>
<Route path='/' exact component={OneUser}/>
<Route path='/post' exact component={Display}/>
</Switch>
</div>
</Router>
</Provider>
);
}
export default App;
OneUser.js
import React , {useState, useEffect} from 'react'
import {oneFetchData} from '../redux/oneAction'
import {connect} from 'react-redux'
function OneUser({user,oneFetchData, history}) {
const [id,setId] = useState(1)
const [IdFromButton,setIdFromButton] = useState(1)
const submit = () => {
setIdFromButton(id);
history.push("/post")
}
useEffect( () =>{
oneFetchData(id)
},[IdFromButton]
)
return(
<div>
<input type="text" value={id} onChange={e=> setId(e.target.value)}>
</input>
< button onClick={submit}>Fetch Post</button>
</div>
)
}
const gettingValue = state => {
return{
user: state.data
}
}
const callingAction = dispatch => {
return{
oneFetchData: (id)=>
dispatch(oneFetchData(id))
console.log('dispatched') // not dispatching here function
}
}
Display.js
import React from 'react'
import {useSelector} from 'react-redux'
function Display({history}) {
const user = useSelector( state => state.data)
return (
<div>
<h1>
Title from another component - {user.title}
</h1>
</div>
)
}
export default Display

I think your component may be unmounted before the state changes. To fix that, you should call history.push after IdFromButton has changed.
const [id,setId] = useState(1)
const [IdFromButton,setIdFromButton] = useState(1)
const submit = () => {
setIdFromButton(id);
}
useEffect( () =>{
oneFetchData(id)
history.push("/post")
},[IdFromButton]
)

Related

Why im able to use my variable in one component and not in another? useContext

Im using use context for storing user data when i loggin. I have the following context component, LoginContext.js:
import React, { createContext, useState } from "react";
import jwt_decode from 'jwt-decode';
const LoginContext = createContext();
export function LoginProvider({children}) {
const [user, setUser] = useState({})
const [isLoggedIn, setIsLoggedIn ] = useState(false)
function handleCallbackResponse(response){
var userData = jwt_decode(response.credential); //Token with the login user Data
setUser(userData); //Store user Data
setIsLoggedIn(true)
/* console.log(userData) */
document.getElementById('signInDiv').hidden = true;
}
function handleSignOut(event) {
setUser({}) //Empy the user Data
document.getElementById('signInDiv').hidden = false;
}
return(
<LoginContext.Provider value={{user, handleCallbackResponse, handleSignOut}}>{children}</LoginContext.Provider>
);
}
export default LoginContext
In my Login.js im using the context above with it functions and properties.
/*global google*/
import React, { useContext, useEffect } from 'react'
import LoginContext from '../LoginContext';
const Login = () => {
const {user, handleCallbackResponse, handleSignOut} = useContext(LoginContext);
useEffect(()=>{
google.accounts.id.initialize({
client_id:"MY client ID",
callback: handleCallbackResponse
})
google.accounts.id.prompt();
window.google.accounts.id.renderButton(
document.getElementById('signInDiv'),
{theme: 'outline', size: 'medium'}
)
}, []);
return (
<div>
<div id="signInDiv"></div>
{
//If user objetc is not empty show sign out button
Object.keys(user).length !== 0 &&
<button onClick={(e)=>handleSignOut(e)}>Sign Out</button>
}
{user &&
<div>
<img src={user.picture} alt="" />
<h3>{user.name}</h3>
</div>
}
</div>
)
}
export default Login
As you can see im accessing to user prop or functions such as handleCallbackResponse that come from my LoginContext.js
But now in my Dashboard.js im trying to use the user prop from LoginContex.js but is not rendering anything.
import React, { useContext } from 'react'
import Navbar from '../organisms/Navbar'
import './Dashboard.css'
import LoginContext from '../LoginContext';
const Dashboard = () => {
const {user} = useContext(LoginContext)
console.log(user)
return (
<div className='dashboard'>
<h2>{user.name}</h2>
<Navbar/>
<div className='content'>
<h1>Welcome to my Dashboard</h1>
</div>
</div>
)
}
export default Dashboard
In my h2 tag im trying to render the user.name but it doesn´t renders anything.
App.js:
import './App.css';
import { useContext } from 'react';
import Login from './atoms/Login';
import { BrowserRouter , Routes, Route } from 'react-router-dom';
import Dashboard from './pages/Dashboard';
import { LoginProvider } from './LoginContext';
function App() {
return (
<LoginProvider >
<BrowserRouter>
<Routes>
<Route exact path="/dashboard" element={ /* isLoggedIn ? */<Dashboard/> /* : <Login /> */}/>
<Route path="/" element={<Login /* setIsLoggedIn={setIsLoggedIn} *//>} />
</Routes>
</BrowserRouter>
</LoginProvider>
);
}
export default App;
I did a console.log of user.name and i get undefined.

React component not getting rendered without a reload --using <Navigate/>

I am using react 18 ,redux and react-router dom to navigate to and from pages. HOwever after successful login the screen first shows the homepage for a split second then a loader and then the screen completely goes white. But after only pressing reload manually once the Home component is rendered again. Also my Home page has a lot of nested components in its tree passing props to each other's child.
I wonder if it has something to do with the redux action dispatches?
How to make the Home page load in the first go itself?
Here is my App.js=>
import { Fragment, useEffect, useState } from 'react';
import {BrowserRouter as Router , Routes , Route , Navigate} from 'react-router-dom';
import './App.css';
import Header from './components/Header/Header';
import Home from './components/Home/Home.js';
import LandingPage from './components/Landing/Landing.js'
import Login from './components/Login/Login';
import { useDispatch, useSelector } from 'react-redux';
import { loadUser } from './Actions/UserActions';
import Account from './components/Account/Account.js'
function App() {
const dispatch = useDispatch();
useEffect(() => {
dispatch(loadUser());
}, [dispatch])
const {isAuthenticated} = useSelector(state=>state.user);
const [reload, setReload] = useState(false);
return (
<Fragment>
<Router>
{isAuthenticated && <Header/>}
<Routes>
<Route path='/' element={!isAuthenticated? (<Navigate to='/login' replace={true} />): (<Home/>)}/>
<Route path='/welcome' element={<LandingPage/>}/>
<Route path='/login' element={!isAuthenticated? <Login/> :(<Navigate to='/' replace={true}/>)}/>
<Route path='/account' element={!isAuthenticated? (<Navigate to='/' />): <Account/>}/>
</Routes>
</Router>
</Fragment>
);
}
export default App;
Here is my Home component=>
import React, { useEffect, useState } from 'react'
import './Home.css'
import User from '../User/User.js'
import Post from '../Post/Post.js'
import { useDispatch, useSelector } from 'react-redux'
import { getAllUsers, getPosts } from '../../Actions/UserActions'
import Loader from '../Loader/Loader'
import { Typography } from '#mui/material'
import Metadata from '../Metadata/Metadata'
const Home = () => {
const dispatch=useDispatch();
const { loading,posts,error} = useSelector(state =>state.getPosts)
const { usersLoading , users ,userError} =useSelector(state=>state.allUsers)
const { error: likeError, message } = useSelector((state) => state.like);
useEffect(() => {
dispatch(getPosts());
dispatch(getAllUsers());
}, [dispatch])
useEffect(() => {
if (error) {
dispatch({ type: "clearErrors" });
}
if (likeError) {
dispatch({ type: "clearErrors" });
}
}, [alert, error, message, likeError, dispatch]);
return loading===true||usersLoading===true? (
<Loader />)
:(
<div className="home">
<Metadata title='Civilised' />
<div className="homeleft">
{
posts&&posts.length>0 ? posts.map((post)=>
(<Post
key={post._id}
postId={post._id}
caption={post.caption}
postImage={post.image.url}
likes={post.likes}
isLiked={false}
comments={post.comments}
ownerImage={post.owner.avatar.url}
ownerName={post.owner.name}
ownerId={post.owner._id}
/>)
): (<Typography variant='h6'>No posts</Typography>)
}
</div>
<div className="homeright">
{
users ? users.map((user)=>(
<User
key={user._id}
userId={user._id}
name={user.name}
avatar={'https://media-exp1.licdn.com/dms/image/C5603AQHlHovW8nMkFA/profile-displayphoto-shrink_200_200/0/1610020467592?e=2147483647&v=beta&t=vBPLqLRHM1Py_hRw7vSbT86TKE7UREGqCFyvoYGyJoc'}
//avatar={currentUser.avatar.url}
/>
)) : null
}
</div>
</div> )
}
export default Home
Here are the warnings I face just after a successful login when the page goes blank=>
Here are the warning I get in the console after I reload the page and the is successfully rendered and functioning well.=>

React-Router-Dom <Link> not render page

I'm building a practice app that uses Unsplash to render users photos. I'm using React and Redux. With react-router-dom, I'm trying to follow the docs but I find it very confusing to set up. Here's what I have so far. When I click on a result out of a returned list of results from a search, I want it to render a user page profile.
index.js (make sure I have react-router-do set up correctly):
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import './index.css';
import App from './App';
// import store from './app/store';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import reducers from "./app/reducers/rootReducer";
import * as serviceWorker from './serviceWorker';
const storeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducers, storeEnhancers(applyMiddleware(thunk)));
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
Top component App
import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Images from "./app/components/Images";
import Search from "./app/components/Search";
import UserProfile from "./app/components/UserProfile";
import "./App.css";
function App() {
return (
<>
<Search />
<Images />
<Router>
<Route link="/userProfile">
<UserProfile />
</Route>
</Router>
</>
);
}
export default App;
search (parent component to searchResults where exists):
import React, { useState, useEffect } from "react";
import { connect } from "react-redux";
import { queryAction } from "../actions/queryAction";
import SearchResults from "./SearchResults";
const Search = (props) => {
const [query, setQuery] = useState("");
console.log(props.searches);
const searchPhotos = async (e) => {
e.preventDefault();
console.log("submitting form");
props.queryAction(query);
};
const showUsers = (user, e) => {
e.preventDefault()
console.log(user)
};
return (
<>
<form className="form" onSubmit={searchPhotos}>
<label className="label" htmlFor="query">
{" "}
</label>
<input
type="text"
name="query"
className="input"
placeholder={`Try "dog" or "apple"`}
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<button type="submit" className="button">
Search
</button>
</form>
<SearchResults results={props.searches} showUsers={showUsers} />
</>
);
};
const mapStateToProps = (state) => {
return {
searches: state.searches,
};
};
const mapDispatchToProps = (dispatch) => {
return {
queryAction: (entry) => dispatch(queryAction(entry)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Search);
searchResults:
import React from "react";
import { BrowserRouter as Router, Link } from "react-router-dom";
import { getUserAction } from "../actions/getUserAction";
import { connect } from "react-redux";
const SearchResults = (props) => {
const { results } = props.results.searches;
const handleClick = (result, e) => {
e.preventDefault();
props.getUser(result.username);
};
return (
<>
{results &&
results.map((result, id) => {
return (
<div key={id}>
<Router>
<Link to="/userProfile" onClick={(e) => handleClick(result, e)}>
{result.username}
</Link>
</Router>
</div>
);
})}
</>
);
};
const mapDispatchToProps = (dispatch) => {
return {
getUser: (query) => dispatch(getUserAction(query)),
};
};
export default connect(null, mapDispatchToProps)(SearchResults);
and finally the UserProfile component:
import React from 'react';
import { connect } from 'react-redux';
const UserProfile = props => {
console.log(props)
return (
<div>
</div>
);
}
const mapStateToProps = state => {
return {
user: state.users
}
}
export default connect(mapStateToProps, null)(UserProfile);
app component
import React from "react";
import { Switch, Route } from "react-router-dom";
import Images from "./app/components/Images";
import Search from "./app/components/Search";
import UserProfile from "./app/components/UserProfile";
import "./App.css";
function App() {
return (
<>
<Search />
<Images />
<Switch>
<Route path="/userProfile/:username">
<UserProfile />
</Route>
</Switch>
</>
);
}
export default App;
SearchResults component
import React from "react";
import { Link } from "react-router-dom";
const SearchResults = (props) => {
const { results } = props.results.searches;
const handleClick = (result, e) => {
e.preventDefault();
props.getUser(result.username);
};
return (
<>
{results &&
results.map((result, id) => {
return (
<div key={id}>
<Link to={`/userProfile/${result.username}`}>
{result.username}
</Link>
</div>
);
})}
</>
);
};
export default SearchResults;
UserProfile component
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { getUserAction } from "../actions/getUserAction";
const UserProfile = props => {
useEffect(() => {
props.getUserAction(props.match.params.username)
},[])
console.log(props)
return (
<div>
{props.user
? <div>{user.username}</div>
: <div>Loading...</div>
}
</div>
);
}
const mapStateToProps = state => {
return {
user: state.users
}
}
const mapDispatchToProps = (dispatch) => {
return {
getUser: (query) => dispatch(getUserAction(query)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(UserProfile);
Edit: Add a param to your link and remove the onclick. Update the Route to expect a :username param. You can access the param through props in UserProfile component.
Make sure to perform the action or access state when mounting the UserProfile component so you have some data when it renders.
Edit 2: Added UserProfile component to answer. You want to dispatch your action when the component is mounting. Also, set a ternary to show "Loading..." if state.user isn't done being fetched.

React: Cannot update a component from inside the function body of a different component

i'm trying to only render the component <IntercomClient /> after a user clicks "Accept" on a cookie consent banner. Clicking accept changes the GlobalLayout's intercomIsActive state to true and thereby renders the IntercomClient. This is working but the warning concerns me.
How can I workaround the child/parent state change? I've been looking around but don't really understand.
import React, { useState } from 'react'
import { CookieBanner } from '#palmabit/react-cookie-law'
import IntercomClient from '../components/intercomClient'
const GlobalLayout = ({ location, children }) => {
const [intercomIsActive, setIntercomIsActive] = useState(false)
return (
...
<CookieBanner
onAccept={() => setIntercomIsActive(true)}
/>
<IntercomClient active={intercomIsActive}/>
...
)}
IntercomClient
import React from 'react';
import Intercom from 'react-intercom'
const IntercomClient = ({ active }) => {
return active ? <div><Intercom appID="XXXXXX" /></div> : null
}
export default IntercomClient;
import React, {useState} from 'react';
const Example = () => {
const [intercomIsActive, setIntercomIsActive] = useState(false)
return (
<Layout>
...
<CookieBanner
onAccept={() => setIntercomIsActive(true)}
/>
<IntercomClient active={intercomIsActive}/>
...
</Layout>
);
};
export default Example;
import React, {useState} from 'react';
const Example = () => {
const [intercomIsActive, setIntercomIsActive] = useState(false)
return (
<Layout>
...
<CookieBanner
onAccept={() => setIntercomIsActive(true)}
/>
{
intercomIsActive &&
<IntercomClient active={intercomIsActive}/>
}
...
</Layout>
);
};
export default Example;

Switch to results Component `onSubmit` a search form in react hooks using react router

Hi I have a scenario where I put a search bar on the top nav so a user can search from anywhere in the app. How to do I switch to the results component once the user submits the search form? Here's my search component that populates the global state with search results but I can't manage to switch the view to the results component.
import React, { useState, useEffect, useContext } from 'react';
import axios from 'axios';
import { StateContext } from '../../StateContext';
import './SearchBar.scss';
import sprite from '../../assets/icons/sprite.svg';
function SearchBar() {
const [state, setState] = useContext(StateContext);
const [userInput, setUserInput] = useState('');
const [bookName, setBookName] = useState('');
useEffect(() => {
axios
.get(`https://www.googleapis.com/books/v1/volumes?q=${bookName}`)
.then((res) => {
let book_list = res.data.items;
setState({
book_list: book_list,
heading: 'Search Results'
});
})
.catch((err) => console.log(err));
}, [bookName]);
const findBook = (e) => {
e.preventDefault();
setBookName(userInput);
};
const onChange = (e) => {
setUserInput(e.target.value);
};
return (
<form className='searchbar' onSubmit={findBook}>
<input
type='search'
className='searchbar__input'
placeholder='Search for a book'
value={userInput}
onChange={onChange}
/>
<button className='searchbar__button'>
<svg className='searchbar__icon'>
<use xlinkHref={`${sprite}#icon-search`} />
</svg>
</button>
</form>
);
}
export default SearchBar;
Here's how I'm handling routing:
import React from 'react';
import Nav from './components/Nav/Nav';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Books from './containers/Books';
import Book from './containers/Book';
import { ContextController } from './StateContext';
function App() {
return (
<ContextController>
<Router>
<div className='app'>
<Nav />
<main>
<Switch>
<Route exact path='/' component={Books} />
<Route exact path='/book/:id' component={Book} />
</Switch>
</main>
</div>
</Router>
</ContextController>
);
}
export default App;
If you have a dedicated route for search results, try this in your ContextController
import { useHistory } from 'react-router-dom';
// later
const history = useHistory();
React.useEffect(() => {
if (state?.book_list?.length > 0) {
history.push('/search-results');
}
}, [state]);
Also, it is important to note that the Router should be on top of your Data Context;
Because if you want to access the history from the a tree, it needs to be wrapped in a Router, or else it will return undefined as a value for history
Here is a working codesandbox

Resources