Passing a function works, but doesn't change the variable - reactjs

I'm passing a function to another component in the link. In another component, I make a query to api, when the query is executed I go back to the previous page and execute the "openFromLogin" function. The function executes because it returns console.log, but the variable is still false.
I want to do so that after logging in and redirecting it automatically opens the modal.
Please help, thanks :)
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import Modal from 'react-modal';
function ProductSidebarOne(props) {
const [openPricing, setOpenPricing] = useState(false);
const openFromLogin = () => {
setOpenPricing(true);
console.log("done");
}
console.log(openPricing);
return (
<>
<Link to={{ pathname: `/login`, state: { from: props.location.pathname }, openFromLogin }} className="font-orange font-semibold">Log in</Link>
<Modal
isOpen={openPricing}
shouldFocusAfterRender={false}
className="pricing-popup"
closeTimeoutMS={10}
>
<div className="modal-dialog modal-dialog-centered" role="document">
<div className="modal-content">
<div className="modal-body">
<button type="button" className="close" aria-label="Close" style={{ position: 'absolute', right: '0', top: '0' }}>
<span aria-hidden="true">×</span>
</button>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-link btn-sm">Close</button>
<button type="submit" className="btn btn-primary btn-sm">Send</button>
</div>
</div>
</div>
</Modal>
</>
)
}
export default ProductSidebarOne;
import React, { useState } from 'react';
import axios from 'axios';
import { setUserSession } from '../../../../utils';
function Login(props) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const openFromLogin = props.location.openFromLogin;
const handleLogin = (e) => {
e.preventDefault();
axios.post("api url", {
email: email,
password: password
})
.then(response => {
setUserSession(response.data);
props.history.push(props.location.state.from, openFromLogin());
});
}
return (
<div className="login-page">
<form onSubmit={handleLogin} className="mb-0">
<input type="text" className="form-control" value={email} onChange={e => setEmail(e.target.value)} />
<input type="password" className="form-control" value={password} onChange={e => setPassword(e.target.value)} />
<div className="form-footer">
<button type="submit" className="btn btn-primary">Log in</button>
</div>
</form>
</div>
)
}
export default Login;

Duplicated in Pass props in Link react-router
AFAIK you cannot do that with Link. You would have to pass either path or query parameters, since Link is basically just an anchor to an url.
What you could do instead is setting up a "global" state with context which gets manipulated after successfully logging in. Your ProductSidebarOne could then subscribe, "use", that context and react to it, if it fulfills the requirements.
Example: Setup a AuthenticationContext and set loggedIn to true after successfully logging in. useContext for AuthenticationContext in ProductSidebarone and useEffect on loggedIn from the context to setOpenPricing(true).
I hope this clarifies it.
EDIT:
I will try to provide a minimal example, including the missing provider. Note that this is not tested.
import React, { createContext } from 'react';
// Create and export context
export const AuthenticationContext = createContext({
loggedIn: false,
setLoggedIn: () => {},
});
//e.g. App.js
import { AuthenticationContext } from 'PATH';
const App = () => {
...
// Hold state for context
const [loggedIn, setLoggedIn] = useState(false);
return (
...
// Provide context to child components in app
<AuthenticationContext.Provider value={{loggedIn, setLoggedIn}}>
...
</AuthenticationContext.Provider>
)
}
import React, { useState, useContext, useEffect } from 'react';
import { Link } from 'react-router-dom';
import Modal from 'react-modal';
import { AuthenticationContext } from '../../../others/common/context';
function ProductSidebarOne(props) {
const [openPricing, setOpenPricing] = useState(false);
// Connect to context
const { loggedIn } = useContext(AuthenticationContext);
// Here we can use the state inside context
// e.g. react to change in context
useEffect(() => {
if(loggedIn) setOpenPricing(true)
}, [loggedIn])
return (...)
}
import React, { useState, useContext } from 'react';
import axios from 'axios';
import { setUserSession } from '../../../../utils';
import { AuthenticationContext } from '../common/context';
function Login(props) {
...
const { loggedIn, setLoggedIn } = useContext(AuthenticationContext);
const handleLogin = (e) => {
e.preventDefault();
axios.post("api url", {
email: email,
password: password
})
.then(response => {
setUserSession(response.data);
// Manipulate state from any child
setLoggedIn(true);
props.history.push(props.location.state.from);
});
}
return (...)
}
export default Login;

Related

using Redux toolkit and it is still executing the users endpoint twice when it does the refresh

Ilm going through the "The Ultimate Authentication course with C# and React" on Udemy and got to the using redux lesson and the /users and the refresh endpoint should be only loading once but it is still loading multiple times as you can see in screenshot
screenshot here
It should only be loading the users and refresh once based on the instructors' instructions. here is my code:
note: I am using the redux and the redux toolkit.
index.tsx
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import './Interceptors/axios';
import {store} from './redux/store';
import { Provider } from 'react-redux';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
reportWebVitals();
axios.ts
import axios from "axios";
axios.defaults.baseURL = `${process.env.REACT_APP_API_URL}`
let refresh = false;
axios.interceptors.response.use(response => response, async error => {
if(error.response.status === 401 && !refresh){
refresh = true;
const response = await axios.post('/Users/refresh', {}, {withCredentials: true})
if(response.status === 200){
axios.defaults.headers.common['Authorization'] = `Bearer ${response.data.token}`
return axios(error.config)
}
}
refresh = false;
return error;
});
store.ts
import {configureStore} from '#reduxjs/toolkit';
import authReducer from './authSlice'
export const store = configureStore({
reducer: {
auth: authReducer
}
})
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
authSlice.ts
import {createSlice} from '#reduxjs/toolkit';
const initialState = {
value: false
}
export const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
setAuth: (state, action) => {
state.value = action.payload;
}
}
})
export const {setAuth} = authSlice.actions;
export default authSlice.reducer;
login.tsx
import React, { useState } from 'react';
import Button from 'react-bootstrap/Button';
import {ILogin} from './Interfaces/Interfaces';
import {useNavigate} from 'react-router-dom';
import axios from 'axios';
export const Login = () => {
const [login, setLogin] = useState<Partial<ILogin>>({});
async function handleForm(arg:any) {
try {
arg.preventDefault();
console.log(JSON.stringify({ ...login }))
const endpoint = '/Users/Login';
const data = {
...login
};
const config = {
withCredentials: true,
//Credentials: true,
headers: {
"Content-Type": "application/json",
}
};
try {
const response = await axios.post(endpoint, data, config);
if (response.status === 200) {
axios.defaults.headers.common['Authorization'] = `Bearer ${response.data.token}`
}
} catch (error) {
console.log(error);
}
loggedIn(true);
}
catch (err) {
console.log(err);
}
}
const navigate = useNavigate();
const loggedIn = (redirect:boolean) => {
if(redirect)
{
navigate('/home');
}
}
return <main className="form-signin" style={{'width': '300px', 'margin': '0 auto'}}>
<form>
<h1 className="h3 mb-3 fw-normal">Please sign in</h1>
<div className="form-floating">
<input type="text" className="form-control" id="floatingUserName" autoComplete="User Name" placeholder="User123" value={login.UserName || ''} onChange={e => setLogin({ ...login, UserName: e.target.value})}/>
<label htmlFor="floatingInput">User Name</label>
</div>
<div className="form-floating">
<input type="password" className="form-control" id="floatingPassword" autoComplete='current-password' placeholder="Password" value={login.Password || ''} onChange={e => setLogin({ ...login, Password: e.target.value})} />
<label htmlFor="floatingPassword">Password</label>
</div>
<div className="checkbox mb-3">
<label>Remember me </label>
<input type="checkbox" value="remember-me" />
</div>
<Button className="w-100 btn btn-lg btn-primary" type="submit" onClick={e => {
handleForm(e)
}}>Sign in</Button>
</form>
</main>
}
siteNav.tsx
import axios from 'axios';
//import { useEffect, useState } from 'react';
import Button from 'react-bootstrap/Button';
import Container from 'react-bootstrap/Container';
//import Form from 'react-bootstrap/Form';
import Nav from 'react-bootstrap/Nav';
import Navbar from 'react-bootstrap/Navbar';
//import NavDropdown from 'react-bootstrap/NavDropdown';
import { useDispatch, useSelector } from 'react-redux';
import { RootState } from '../redux/store';
import {setAuth} from '../redux/authSlice';
export const SiteNav = () => {
const auth = useSelector((state:RootState) => state.auth.value);
const dispatch = useDispatch();
const logout = async () => {
await axios.post('/Users/Logout', {}, {withCredentials: true});
axios.defaults.headers.common['Authorization'] = ``;
dispatch(setAuth(false));
}
let links;
if(auth){
// handle logout
links = <div className="text-end">
<Button href="/login" className="btn me-2" style={{'color':'white'}} variant="outline-dark" onClick={logout}>Logout</Button>
</div>
}
else
{
//handle login
links = <div className="text-end">
<Button href="/login" className="btn me-2" style={{'color':'white'}} variant="outline-dark">Sign in</Button>
<Button href="/register" className="btn me-2" style={{'color':'white'}} variant="outline-dark">Register</Button>
</div>
}
return (
<Navbar bg="dark" variant="dark" expand="lg">
<Container fluid>
<Navbar.Brand className="light" href="/home"><img src="logo192.png" width="62" height="62" alt="SCSFC"></img> SoCal SportFishing Club</Navbar.Brand>
<Navbar.Toggle aria-controls="navbarScroll" />
<Navbar.Collapse id="navbarScroll">
<Nav
className="me-auto my-2 my-lg-0"
style={{ maxHeight: '100px' }}
navbarScroll
>
<Nav.Link href="#action1">About</Nav.Link>
<Nav.Link href="#action1">Meetings</Nav.Link>
<Nav.Link href="#action2">Charters</Nav.Link>
<Nav.Link href="#action2">Landings</Nav.Link>
</Nav>
{links}
</Navbar.Collapse>
</Container>
</Navbar>
);
}
home.tsx
import axios from 'axios';
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { setAuth } from '../redux/authSlice';
import { RootState } from '../redux/store';
export const Home = () => {
const dispatch = useDispatch()
const auth = useSelector((state:RootState) => state.auth.value);
const [message, setMessage] = useState('')
useEffect(() => {
(async () => {
try{
const {data} = await axios.get(`${process.env.REACT_APP_API_URL}/users`)
setMessage(`Hi ${data.userName}`)
dispatch(setAuth(true));
}
catch (e){
setMessage('You are not logged in.');
dispatch(setAuth(false));
}
})()
}, []);
return (<div className="container mt-5 text-center">
<h3>{auth ? message : 'You are not authenticated'}</h3>
</div>);
}
I was expecting to have the users and refresh only run once on load.

I don't know how to add an id to make relation between my post and my comment (React JS/Strapi)

I use React JS and Strapi. I am trying to send comment to my API. I worked but without one essential thing : the post for which I am writing a comment. How can I possibly add the id of my post so that the relation is made between my comment and my post ?
import React, { useState } from 'react'
import { useParams } from 'react-router-dom'
import TextField from '#material-ui/core/TextField';
import { Button } from '#material-ui/core'
import CommentsAPI from '../../Services/CommentsAPI'
export default function CommentForm() {
const [comment, setComment] = useState({})
const {id} = useParams()
const handleSubmit = async (event) => {
event.preventDefault();
try {
CommentsAPI.create(JSON.parse(`{"data":${JSON.stringify(comment)}}`))
} catch (error) {
console.log(error)
}
}
const handleChange = (event) => {
const {name, value} = event.currentTarget
setComment({
...comment,
[name]: value
})
}
return (
<form onSubmit={handleSubmit}>
<div>
<TextField
id="pseudo"
label="Pseudo"
type="text"
onChange={handleChange}
name="pseudo"
/>
</div>
<div>
<TextField
id="comment"
label="Comment"
multiline
minRows={2}
onChange={handleChange}
name="content"
/>
</div>
<div>
<Button variant="contained" color="primary" type="submit">
Send
</Button>
</div>
</form>
)
}
import { URL_COMMENTS } from '../config'
import axios from 'axios'
function create(id_post, comment) {
return axios.post(URL_COMMENTS, id_post, comment)
}
const CommentsAPI = {
create
}
export default CommentsAPI
Thank you for your help.

passing a variable from child component to parent component in Next.js

I have 2 components home and tiny tiny is imported inside home as u can see in the code
I am trying to pass value.toString("html") from tiny.js to home.js
if this is not possible at least help me integrate both tiny and home components as a single object so that I don't have to pass the value as props to a parent component
import React from "react";
import Tiny from "./tiny";
function Home({ data }) {
const [Questions, setQuestions] = useState();
const [deatils1, setdeatils] = useState();
function clickQuestion() {
axios
.post("https://askover.wixten.com/questionpost", {
Name: Questions,
Summary: deatils1,//pass tiny value as summery
})
.then(() => {
window.location.reload();
});
}
function question(e) {
setQuestions(e.target.value);
}
return (
<>
<div>
<div className="container search-box">
<Form>
<Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
<Form.Label>Title</Form.Label>
<Form.Control
type="text"
onChange={question}
placeholder="ask anything?"
/>
</Form.Group>
<Tiny /> //tiny component
</Form>
<Button
type="submit"
disabled={!deatils1 || !Questions}
onClick={clickQuestion}
variant="outline-secondary"
id="button-addon2"
>
ask?
</Button>
</div>
</div>
</>
);
}
tiny.js
import React, { useState, useEffect } from "react";
import dynamic from "next/dynamic";
import PropTypes from "prop-types";
//import the component
const RichTextEditor = dynamic(() => import("react-rte"), { ssr: false });
const MyStatefulEditor = ({ onChange }) => {
const [value, setValue] = useState([]);
console.log(value.toString("html"));
useEffect(() => {
const importModule = async () => {
//import module on the client-side to get `createEmptyValue` instead of a component
const module = await import("react-rte");
console.log(module);
setValue(module.createEmptyValue());
};
importModule();
}, []);
const handleOnChange = (value) => {
setValue(value);
if (onChange) {
onChange(value.toString("html"));
}
};
return <RichTextEditor value={value} onChange={handleOnChange} />;
};
MyStatefulEditor.propTypes = {
onChange: PropTypes.func,
};
export default MyStatefulEditor;
Actually, you already have onChange event in tiny, so you only need to pass another onChange event from home to tiny.
import React from "react";
import Tiny from "./tiny";
function Home({ data }) {
const [Questions, setQuestions] = useState();
const [details, setDetails] = useState();
function clickQuestion() {
axios
.post("https://askover.wixten.com/questionpost", {
Name: Questions,
Summary: details,//pass tiny value as summery
})
.then(() => {
window.location.reload();
});
}
function question(e) {
setQuestions(e.target.value);
}
return (
<>
<div>
<div className="container search-box">
<Form>
<Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
<Form.Label>Title</Form.Label>
<Form.Control
type="text"
onChange={question}
placeholder="ask anything?"
/>
</Form.Group>
<Tiny onChange={(value) => setDetails(value)}/> //tiny component
</Form>
<Button
type="submit"
disabled={!deatils1 || !Questions}
onClick={clickQuestion}
variant="outline-secondary"
id="button-addon2"
>
ask?
</Button>
</div>
</div>
</>
);
}

How to perform search operation from Navbar when data is recieved from global Context API

I am executing search operation from Navbar component for the data that is present in separate Context API, and the results for the search operation will be presented in another component call Blog, which is using same Context API, but the problem here is search operation is not executing in real time, like when I clear the search bar then It's difficult to set search term in use state hook which is present in context API. So in this case how to solve the problem.
Below is my code from context API
import { BlogContext } from "./BlogContext";
import React from "react";
import { useState } from "react";
export const BlogState = (props) => {
const host = "http://localhost:5000";
const blogInitial = [];
const [blog, setBlog] = useState(blogInitial);
let fetchAllNotes = async () => {
//API call
const response = await fetch(`${host}/api/notes/blog/`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const json = await response.json();
setBlog(json);
};
const searchFilter = (searchWord) => {
const searchTerm =
blog.filter((note) =>
note.description.toLowerCase().includes(searchWord)
) || ((note) => note.title.toLowerCase().includes(searchWord));
setBlog(searchTerm);
};
return (
<BlogContext.Provider value={{ blog, fetchAllNotes, fil, searchFilter }}>
{props.children}
</BlogContext.Provider>
);
};
Code from Navbar component
import React, { useContext, useState } from "react";
import { Link, useNavigate, useLocation } from "react-router-dom";
import { ThemeContext } from "../context/notes/ThemeContext";
import { BlogContext } from "../context/notes/BlogContext";
export const Navbar = () => {
const { searchFilter, blog } = useContext(BlogContext);
const [searchTerm, setSearchTerm] = useState(blog);
const onChange = (e) => {
if (e.target.value === "") {
window.location.reload(true);
} else {
const search = e.target.value.toLowerCase();
setSearchTerm(search);
searchFilter(searchTerm);
}
};
return (
<div>
<nav
<form className="d-flex mx-2">
<input
onChange={onChange}
className="form-control me-2"
type="search"
placeholder="Search"
aria-label="Search"
/>
<button className="btn btn-success mx-2" type="submit">Clear</button>
</form>
</nav>
</div>
);
};
Code from Blog component
import React, { useContext, useEffect } from "react";
import { ThemeContext } from "../context/notes/ThemeContext";
import { BlogContext } from "../context/notes/BlogContext";
import BlogItem from "./BlogItem";
import { FlexNavbar } from "./FlexNavbar";
const Blog = () => {
const { theme } = useContext(ThemeContext);
const { blog } = useContext(BlogContext);
return (
<>
<div
className={`container bg-${theme} text-${
theme === "dark" ? "light" : "dark"
}`}
>
<FlexNavbar className="" />
<div className="row">
{blog.map((notes) => {
return <BlogItem key={notes._id} note={notes} />;
})}
</div>
</div>
</>
);
};
export default Blog;

Back functionality of browser is not working

I have written the following code with a Query string corresponding to the filters applied. On clicking browser's back button I want to get back to the previous set of filters, but instead I land back to the browser's home page every time.
import React, {useState, useEffect, useCallback} from 'react'
import {DropDownList, MultiSelect} from "#progress/kendo-react-dropdowns"
import {Button} from '#progress/kendo-react-buttons'
import { useHistory, useLocation } from "react-router-dom"
const Filter = () => {
const history=useHistory()
const location=useLocation()
const [Standard,setStandard] = useState("All")
const [Hobbies,setHobbies] = useState(["Playing"])
const [Responsibility,setResponsibility] = useState("All")
const [QueryString,setQueryString] = useState(location.search.substring(1))
const options={
StandardList:["All","VI","VII","VIII"],
HobbiesList: ["Playing", "Drawing","Swimming"],
ResponsibilityList:["All","Monitor","Head","Sports Captain"]
}
const handleApply = ()=>{
setQueryString(`Standard=${JSON.stringify(Standard)}&Responsibility=${JSON.stringify(Responsibility)}&IncidentStatus=${JSON.stringify(Hobbies)}`)
}
const backButtonEvent = useCallback((e) => {
e.preventDefault();
history.goBack()
},[])
useEffect(() => {
window.addEventListener('popstate',backButtonEvent);
return ()=>{
window.removeEventListener('popstate',backButtonEvent);
}
},[backButtonEvent])
useEffect(() => {
var params= new URLSearchParams((QueryString)?(QueryString):`Standard=${JSON.stringify(Standard)}&Responsibility=${JSON.stringify(Responsibility)}&IncidentStatus=${JSON.stringify(Hobbies)}`)
history.push({search: params.toString()})
}, [QueryString])
return (
<div>
<label>Standard </label>
<DropDownList data={options.StandardList} defaultValue={"All"} value={Standard}
onChange={(event)=>setStandard(event.target.value)}/>
<label > Hobbies </label>
<MultiSelect data={options.HobbiesList} defaultValue={["Playing"]} value={Hobbies} onChange={(event)=>setHobbies([...event.value])}/>
<label > Responsibility </label>
<DropDownList data= {options.ResponsibilityList} defaultValue= {"All"} value={Responsibility} onChange={(event)=>setResponsibility(event.target.value)} />
<Button id="submitFilter" type="button" onClick={handleApply} > Apply </Button>
</div>
)
}
export default Filter
Why am I getting this behaviour and how can I resolve it?

Resources