prevent values in Fields of redux-from on unsuccessfull api response - reactjs

I want to prevent values in fields of redux-form on unsuccess API response to review user what he has entered wrong but when I submit the form all the values become clear I try destoryonUnmount method in reudForm() but this is not a good approach
import { useDispatch, useSelector } from "react-redux";
import { SubmissionError } from "redux-form";
import Login from "../../components/Login";
function LoginPage() {
const Formdata = async (Formvalues) => {
const responsive = await dispatch(Action.Login(Formvalues));
if (!!responsive.payload.accessToken) {
} else {
throw new SubmissionError({
_error: "Login Failed",
});
}
};
return <Login onSubmit={Formdata} />;
}
export default LoginPage;
here is my form configuration
const validate = (values) => {
const errors = {};
const requiredFields = ["email", "password"];
requiredFields.forEach((field) => {
if (!values[field]) {
errors[field] = "Required";
}
});
return errors;
};
const LoginComponent = reduxForm({
form: "LoginForm",
validate,
forceUnregisterOnUnmount: true,
})(Login);
export default LoginComponent;

I have found the solution by searching it for two to three days because no one answer it
in my routes file, I have to use either React.pureComponent or momo
import React, { Suspense, memo } from "react";
import { Switch, Route } from "react-router-dom";
// Import Containers
import Login from "../container/Login";
const Routes = () => {
return (
<Suspense fallback="Loading......">
<Switch>
<Route exact path="/" component={Authenciated(Login)} />
<Route component={NotFoundPage} />
</Switch>
</Suspense>
);
};
export default memo(Routes);

Related

React useContext is returning undefined

I am trying to use context with my Gatsby project. I have successfully implemented this in my previous project and I have copied the code over to my new project and it's not working as intended.
This is my context.js file:
import React, { useContext, useState } from "react";
const defaultState = {
isLoggedIn: false,
};
const AuthContext = React.createContext();
export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }) {
const [isLoggedIn, setIsLoggedIn] = useState(false);
function toggle() {
console.log("BOO!");
}
const value = {
isLoggedIn,
setIsLoggedIn,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
This is my app.js file:
import React from "react";
import { Router } from "#reach/router";
import IndexPage from "./index";
import ProjectPage from "./project";
import { AuthProvider } from "../contexts/context";
const App = () => (
<AuthProvider>
<Router basepath="/app">
<IndexPage path="/" component={IndexPage} />
<ProjectPage path="/project" component={ProjectPage} />
</Router>
</AuthProvider>
);
export default App;
This is my index.js file:
import React, { useContext } from "react";
import { Link } from "gatsby";
import { useAuth } from "../contexts/context";
import { AuthContext } from "../contexts/context";
const IndexPage = () => {
console.log(useAuth())
return (
<div className="w-40 h-40 bg-red-400">
{/*<Link to="/project">to projects</Link>*/}
<div>Click me to toggle: uh</div>
</div>
);
};
export default IndexPage;
useAuth() should return the desired components and functions but instead is always returning undefined. I have looked over my previous code as well as snippets on stack overflow and I can't seem to find the correct fix.
The following includes code that successfully built and executed:
Original context.js
import '#stripe/stripe-js'
/* Functionality */
import React, { useContext, useEffect, useState } from "react";
import { navigate } from "#reach/router";
import firebase from 'gatsby-plugin-firebase';
import { useLocalStorage } from 'react-use';
const AuthContext = React.createContext()
export function useAuth() {
return useContext(AuthContext)
}
export function AuthProvider({ children }) {
const [isLoggedIn, setIsLoggedIn] = useState(false)
const [isLoading, setIsLoading] = useLocalStorage("loading", false);
// Sign In
const signInWithRedirect = (source) => {
let provider;
switch(source) {
case 'Google':
provider = new firebase.auth.GoogleAuthProvider()
break;
case 'Github':
provider = new firebase.auth.GithubAuthProvider()
break;
default:
break;
}
setIsLoading(true)
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.SESSION)
.then(() => {
// Existing and future Auth states are now persisted in the current
// session only. Closing the window would clear any existing state even
// If a user forgets to sign out.
// ...
// New sign-in will be persisted with session persistence.
return firebase.auth().signInWithRedirect(provider)
})
.catch((error) => {
// Handle Errors here.
let errorCode = error.code;
let errorMessage = error.message;
});
}
// Sign Out
const signOut = () => {
firebase.auth().signOut().then(() => {
// Sign-out successful.
setIsLoggedIn(false)
navigate('/app/login')
}).catch((error) => {
// An error happened.
});
}
useEffect(() => {
firebase.auth().onAuthStateChanged((user) => {
try {
// If user is authenticated
if (!!user) {
// Fetch firestore document reference
var docRef = firebase.firestore().collection("study_guide_customers").doc(user.uid)
docRef.get().then((doc) => {
console.log('checking doc')
// If the document doesn't exist, create it and add to the firestore database
if (!doc.exists) {
console.log('inside customer')
const customer = {
customerCreationTimestamp: firebase.firestore.Timestamp.now(),
username: user.displayName,
email: user.email
}
firebase.firestore().collection("study_guide_customers").doc(user.uid).set(customer)
.then(() => {
// After docuement for user is created, set login status
setIsLoggedIn(!!user)
setIsLoading(false)
})
.catch((error) => {
console.error("Error writing document: ", error);
});
// If document for user exists, set login status
} else {
setIsLoggedIn(!!user)
setIsLoading(false)
}
})
}
} catch {
console.log('Error checking firestore existence and logging in...')
}
})
}, [isLoggedIn, isLoading, setIsLoading, setIsLoggedIn])
const value = {
signOut,
isLoggedIn,
isLoading,
setIsLoading,
setIsLoggedIn,
signInWithRedirect,
}
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
)
}
Original app.js
/* Stripe Security */
import '#stripe/stripe-js'
/* Functionality */
import React from "react"
import { Router } from "#reach/router"
import PrivateRoute from "../components/PrivateRoute"
import Profile from "../components/Profile"
import Login from "../components/Login"
import Projects from "../components/Projects"
import IndexPage from "./index"
import NotFoundPage from './404'
import { AuthProvider } from "../contexts/context"
const App = () => (
<AuthProvider>
<Router basepath="/app">
<PrivateRoute path="/profile" component={Profile} />
<Login path="/login" component={Login}/>
<IndexPage path="/" component={IndexPage}/>
<Projects path="/projects" component={Projects} />
</Router>
</AuthProvider>
)
export default App
Original index.js
/* Stripe Security */
import '#stripe/stripe-js'
/* Functionality */
import * as React from "react"
import IndexContact from "../components/Index/Contact"
import IndexSelectedProjects from "../components/Index/SelectedProjects"
import IndexFeaturedProjects from "../components/Index/FeaturedProjects"
import IndexFooter from "../components/Index/Footer"
import IndexStudyGuide from "../components/Index/StudyGuide"
import IndexNavbar from "../components/Index/Navbar"
import IndexHeader from "../components/Index/Header"
import IndexAbout from '../components/Index/About'
import IndexExperience from '../components/Index/Experience'
import { useMount } from 'react-use';
const IndexPage = () => {
useMount(() => localStorage.setItem('loading', false));
return (
<>
<IndexNavbar />
<IndexHeader />
<IndexAbout />
<IndexExperience />
<IndexFeaturedProjects />
<IndexSelectedProjects />
<IndexStudyGuide />
<IndexContact />
<IndexFooter />
</>
)
}
export default IndexPage
Then in any component I could simply use the following code to access the context
import { useAuth } from "../contexts/context"
const { isLoggedIn, signInWithRedirect, isLoading } = useAuth()
Child components are mounted before parent. Fix your context.js file to add a default value for isLoggedIn state:
const defaultState = {
isLoggedIn: false,
setIsLoggedIn: () => {}
};
const AuthContext = React.createContext(defaultState);
Your defaultState should also include default methods for any parts of the context you wish to work with.

How to fetch data in a functional react JS component?

I am developing a React JS, Redux, GraphQL, TypeScript app.
And I would like to know how to invoke the function that fetches data and updates the state via GraphQL from my container.
The name of the action that loads the data via GraphQL is appActions.getAppData();
But it causes an infinite refresh loop because it triggers (StatusActions.startAppLoading()); which updates the state as well.
I would like to know how to fix this issue or how to rewrite /Main/index.tsx as a class component and invoke startAppLoading() from componentDidMount().
Thank you in advance.
main.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createBrowserHistory } from 'history';
import { configureStore } from 'app/store';
import { Router } from 'react-router';
import { App } from './app';
// prepare store
const history = createBrowserHistory();
const store = configureStore();
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<App />
</Router>
</Provider>,
document.getElementById('root')
);
app/index.tsx
import React from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import { App as Main } from 'app/containers/Main';
import { hot } from 'react-hot-loader';
let currentContainer = Main;
export const App = hot(module)(() => (
<Switch>
<Route exact path="/" component={currentContainer} />
<Route path="*">
<Redirect to="https://google.com" />
</Route>
</Switch>
));
app/containers/Main/index.tsx
import React from 'react';
import style from './style.css';
import { RouteComponentProps } from 'react-router';
import { useDispatch, useSelector } from 'react-redux';
import { useTodoActions } from 'app/actions';
import { useAppActions } from 'app/actions';
import { RootState } from 'app/reducers';
import { Header, TodoList, Footer } from 'app/components';
export namespace App {
export interface Props extends RouteComponentProps<void> {}
}
export const App = ({ history, location }: App.Props) => {
const dispatch = useDispatch();
const appActions = useAppActions(dispatch);
const { apps } = useSelector((state: RootState) => {
return {
apps: state.apps
};
});
appActions.getAppData();
return (
<div className={style.normal}>
<Header />
<TodoList appActions={appActions} apps={apps} />
<Footer />
</div>
);
};
app/actions/apps.ts
export const getAppData = () => {
let appKey = 'interpegasus';
return (dispatch: Dispatch) => {
dispatch(StatusActions.startAppLoading());
debugger;
apolloClient
.query({
query: gql`
query getApp($appKey: String!) {
getApp(id: $appKey) {
id
name
domain
}
}
`,
variables: {
appKey: appKey
}
})
.then((result) => {
debugger;
if (result.data.apps.length > 0) {
dispatch(populateAppData(result.data.apps[0]));
}
dispatch(StatusActions.endAppLoading());
})
.catch((error) => {
dispatch(StatusActions.endAppLoading());
console.log({
error: error
});
});
};
};
You should put your appActions.getAppData() inside useEffect hooks like this
useEffect(()=>{
appActions.getAppData()
},[])
check the official docs Introducing Hooks
In Main/index.tsx, you are calling appActions.getAppData(); which will lead you to actions/apps.ts. Here, you are doing dispatch(StatusActions.startAppLoading()); which will update the state and re-render ``Main/index.tsx`. Then again you call getAppData() and the loop continues to lead to infinite loop.
Call the api only if not loading.
Something like this:
...
const { apps, loading } = useSelector((state: RootState) => {
return {
apps: state.apps,
loading: state.loading // <----- replace with your actual name of your state
};
});
if(!loading){
appActions.getAppData();
}
...

React.js page does not switch

I'm trying to control the page using react-hook, react-router-dom, redux.
The login screen is implemented and the code that tries to switch to the main page when the login is successful is written.
So I used history.push of react-router.
However, history.push only changes the browser url, not the actual information displayed on the page.
The code is shown below.
In brief code explanation,
The id and password are put into redux and get as getelementbyuId and sent as form data.
The login function works normally.
history.push ('/') written to "userUpdateUserInfo" doesn't work at all.
Only the url of the browser changes, not the main page.
App.tsx
import React from 'react';
import { Route, Switch } from 'react-router-dom';
import { Main, Login } from './pages';
import './css/App.css';
const App: React.FC = () => {
return (
<div>
<div className="App-contents-area">
<Switch>
<Route exact path="/" component={Login} />
<Route exact path="/main" component={Main} />
{/* <Redirect path="*" to="/" /> */}
</Switch>
</div>
</div>
);
}
export default App;
LoginPage.tsx
import React from 'react';
import { Login } from 'Component';
function LoginPage() {
return (
<Login />
);
}
export default LoginPage;
Login.tsx (components)
import React from 'react';
import {
LoginTitle, LoginAvatar, LoginUserId, LoginUserPassword, LoginButton
} from '..';
import '../../css/Login.css';
function Login() {
return (
<div className="Login">
<div className="Login-form-data">
<LoginTitle /> // code skip
<LoginAvatar /> // code skip
<LoginUserId /> // code skip
<LoginUserPassword /> // code skip
<LoginButton />
</div>
</div>
);
}
export default Login;
LoginButton.tsx (components)
import React from 'react';
import { useUpdateUserInfo } from 'Hook';
function LoginButton() {
const { handleLogin } = useUpdateUserInfo(); // custom hook
return (
<div className="LoginButton">
<button className="LoginButton-button" onClick={handleLogin}>Login</button>
</div>
);
}
export default LoginButton;
userUpdateUserInfo.tsx (custom hook)
import { useCallback } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { RootState } from 'Store/modules';
import { updateUserInfo } from 'Store/modules/user';
import { userLoginStatus } from 'Store/modules/login';
import { msg } from 'Lang';
import {
axiosPost, history,
_ShowFail, _ShowSuccess, _ShowSelect
} from 'Module';
export default function useUpdateUserInfo () {
const { id, name, tel, email } = useSelector((state: RootState) => state.user);
let { isLogin } = useSelector((state: RootState) => state.login);
const dispatch = useDispatch();
const handleLogin = useCallback(async () => {
try {
const userId: string = (document.getElementById('LoginUserId-id') as HTMLInputElement).value.trim();
const userPw: string = (document.getElementById('LoginUserPassword-password') as HTMLInputElement).value.trim();
if (!userId.length) { return _ShowFail(msg.pleaseInputUserId); }
if (!userPw.length) { return _ShowFail(msg.pleaseInputUserPassword); }
const formData: FormData = new FormData();
formData.append('userId', userId);
formData.append('userPw', userPw);
const url = '/login/check-login-info';
const config = {
headers: {
'Content-Type': 'multipart/form-data',
},
};
const res = await axiosPost(url, formData, config);
if (res.data.res) {
_ShowSuccess('login success');
const userInfo = {
id: res.data.rows[0].id,
name: res.data.rows[0].name,
email: res.data.rows[0].email,
tel: res.data.rows[0].tel,
};
isLogin = true;
/**************************/
history.push('/main'); // now working
/**************************/
dispatch(updateUserInfo(userInfo));
dispatch(userLoginStatus({ isLogin }));
}
else {
_ShowFail('login fail');
isLogin = false;
dispatch(updateUserInfo({ id, email, name, tel }));
dispatch(userLoginStatus({ isLogin }));
}
}
catch (error) {
_ShowFail(error.message);
}
}, [dispatch]);
return { handleLogin };
};
MainPage.tsx
import React from 'react';
function MainPage() {
return (
<div>
<h2>MainPage!!</h2>
</div>
);
}
export default MainPage;
history.tsx
import { createBrowserHistory } from 'history'
export default createBrowserHistory();
Since last night, I have been suffering from this problem.
How can I change the content of the page?
In App.js your Routes suppose to be inside < Router > < /Router >,
https://reacttraining.com/react-router/web/api/Router
(unless you wrap it on index.js that not including here and App is imported inside there)?

Function not being called in ComponentDidMount

Not really sure how to move forward on this one. I have a (fairly) simple setup with react-router, almost everything works as expected - I get requests and responses between the app and my api - except I have a function I expect to run from my app's componentDidMount lifecycle function, but it's somehow not getting hit...
Here's a pretty minimal version of my code:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Switch } from 'react-router';
import { connect } from 'react-redux';
import { authenticate, unauthenticate } from './actions/session';
import RouteAuthenticated from './RouteAuthenticated'; // Route if authenticated
import RedirectAuthenticated from './RedirectAuthenticated'; // Redirect to login if not authenticated
import Home from './Home';
import Login from './Login';
import Signup from './Signup';
class App extends Component {
componentDidMount() {
const token = localStorage.getItem('token'); // eslint-disable-line no-undef
console.info(token); // <==== Shows a token, great!
console.info(this.props.authenticate.toString()); // Output below
if (token) {
console.info(`We have a token`); // <=== I get to here
this.props.authenticate(); // <=== Never gets run...????
} else {
this.props.unauthenticate();
}
}
render() {
const { isAuthenticated, willAuthenticate } = this.props;
const authProps = { isAuthenticated, willAuthenticate };
return (
<div className="App">
<Header />
<Switch>
<RouteAuthenticated exact path="/" component={Home} {...authProps} />
<RedirectAuthenticated exact path="/login" component={Login} {...authProps} />
<RedirectAuthenticated exact path="/signup" component={Signup} {...authProps} />
</Switch>
</div>
);
}
}
App.propTypes = {
authenticate: PropTypes.func.isRequired,
unauthenticate: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool.isRequired,
willAuthenticate: PropTypes.bool.isRequired
};
const mapStateToProps = (state) => ({
isAuthenticated: state.isAuthenticated,
willAuthenticate: state.willAuthenticate
});
const mapDispatchToProps = () => ({
authenticate,
unauthenticate
});
export default connect(mapStateToProps, mapDispatchToProps)(App);
And since this seemed super weird, I decided to go ahead and just make sure that there actually was a function to run. I put in the console.info(this.props.authenticate.toString()); line above, and here's what I get, which looks fine except that it never actually runs:
function authenticate() {
return function (dispatch) {
console.info("auth function");
dispatch({
type: 'AUTHENTICATION_REQUEST'
});
try {
console.info("AUTHing");
setCurrentUser(dispatch, _api2.default.get('is-auth'));
} catch (e) {
console.info("AUTH ERROR");
localStorage.removeItem('token'); // eslint-disable-line no-undef
window.location = '/login'; // eslint-disable-line no-undef
}
};
}
And here is the original function:
export const authenticate = () => (dispatch) => {
console.info("auth function"); // <=== never called?
dispatch({
type: 'AUTHENTICATION_REQUEST'
});
try {
console.info("AUTHing");
setCurrentUser(dispatch, api.get('is-auth'));
} catch (e) {
console.info("AUTH ERROR");
localStorage.removeItem('token');
window.location = '/login';
}
};
And the store:
import { createStore, applyMiddleware } from 'redux';
import { routerMiddleware } from 'react-router-redux';
import thunk from 'redux-thunk';
import history from './history';
import reducers from './reducers';
const middleWare = [thunk, routerMiddleware(history)];
const createStoreWithMiddleware = applyMiddleware(...middleWare)(createStore);
const store = createStoreWithMiddleware(reducers);
export default store;
Any ideas what I can try from here?

React-Redux User State is not getting updated

I have a state that is getting the right object from my action but It won't seem to actually append to the state. Does anyone have any experience working with reducers in React-Redux that can lend a hand? I'm not sure why I can't return the new state.
Here is the code in progress I have at the moment.
import * as types from '../constants'
const defaultState = {}
const userReducer = (state = defaultState, action) =>{
switch(action.type){
case types.USER_LOGGED_IN:
console.log("in the logged in reducer")
console.log(action.cookie)
return {
...state,
cookie: action.cookie
}
case types.USER_LOGGED_OUT:
return state
default:
return state
}
}
export default userReducer
The console.log will actually print out the correct cookie value for me. Any ideas or help would be greatly appreciated.
per Request here is the container,
import React, { Component, PropTypes } from 'react'
import { routerActions } from 'react-router-redux'
import { connect } from 'react-redux'
import GoogleLogin from '../components/GoogleLogin'
import actions from '../actions/'
import cookie from 'react-cookie'
const login = actions.userActions.login
function select(state, ownProps) {
const isAuthenticated = state.user.cookie || false
console.log(isAuthenticated)
const redirect = ownProps.location.query.redirect || '/'
return {
isAuthenticated,
redirect
}
}
class LoginContainer extends Component {
componentWillMount() {
const { isAuthenticated, replace, redirect } = this.props
if (isAuthenticated) {
replace(redirect)
}
}
componentWillReceiveProps(nextProps) {
const { isAuthenticated, replace, redirect } = nextProps
const { isAuthenticated: wasAuthenticated } = this.props
if (!wasAuthenticated && isAuthenticated) {
replace(redirect)
}
}
onClick(e){
e.preventDefault()
//console.log("in the onClick")
var status = cookie.load("MarketingStatsApp",false)
if(!(status == undefined)){
const login = actions.userActions.login
this.props.login({
cookie: status
})
}
window.location.href='auth/google'
};
render() {
return (
<div>
<h1>Login using Google</h1>
<GoogleLogin onClick={this.onClick.bind(this)}/>
</div>
)
}
}
export default connect(select, { login, replace: routerActions.replace })(LoginContainer)
Here you can see the false get printed out the first time through and then the cookie get printed out. You can even see the action but I can't click into the arrows for that action and the the other ones don't display the updated state as how I expect it to be
Thanks for the help everyone
The problem ended up being on my double verification on my router page. I needed to re-bring in the action variable for it to launch properly. Here is the code for that
import { Router, Route, browserHistory,IndexRedirect } from 'react-router'
import React from 'react';
import Layout from './components/Layout'
import Home from './containers/Home'
import Login from './containers/LoginContainer'
import Dashboard from './containers/Dashboard'
import { UserIsAuthenticated} from './util/wrapper'
import cookie from 'react-cookie'
import axios from 'axios'
import actions from './actions'
import store from './reducers';
const Authenticated = UserIsAuthenticated((props) => props.children)
function doubleCheckGUID(guid){
return axios.post("/auth/guid",{guid: guid}).then(response => {
const login = actions.userActions.login
store.dispatch(login({
cookie: cookie.load("MarketingStatsApp",false)
}))
return true;
}).catch(error => {
return false;
})
}
if(cookie.load("MarketingStatsApp",false)){
doubleCheckGUID(cookie.load("MarketingStatsApp",false))
}
const AppRouter = () =>
<Router history={browserHistory} >
<Route path="/" component={Layout}>
<IndexRedirect to="/home" />
<Route path="home" component={Home} />
<Route path="login" component={Login}/>
<Route component={Authenticated} >
<Route path="dash" component={Dashboard} />
</Route>
</Route>
</Router>
export default AppRouter

Resources