GOAL: Give data to App comp from Register comp, then from App comp to Chat comp
Register --> App --> Chat
Additional info: Register is taking a username and then passing it to Chat comp to render as username
Or should I just pass the value to url params and then get it?
The answers I looked up were suggesting creating redux or were from class components
import Chat from "./components/chat";
import Register from "./components/register";
import { BrowserRouter, Route, Routes } from "react-router-dom";
function App(props) {
console.log(props);
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Register />}>
<Route
path="chat"
element={(props) => <Chat {...props} data={true} />}
/>
</Route>
<Route
path="*"
element={
<main style={{ padding: "1rem" }}>
<p>404 :)</p>
</main>
}
/>
</Routes>
</BrowserRouter>
);
}
Register component:
import { useState } from "react";
import { useNavigate } from "react-router-dom";
export default function Register() {
const navigate = useNavigate();
const [name, setName] = useState("");
const generatedName = `user#${Math.floor(Math.random() * 1000000 + 1)}`;
const handleSubmit = () => {
if (name === "") {
setName(generatedName);
}
navigate("/chat", { replace: true });
HERE I WANT TO RETURN THE DATA TO PARENT//return "hello parent";
};
return (
<div>
Enter username:{" "}
<input
placeholder={generatedName}
onChange={(e) => setName(e.target.value)}
></input>
<button onClick={() => handleSubmit()}>enter</button>
</div>
);
}
You can set up a data state in your app component using a useState hook and pass a reference to a setter function which modifies your data and set it to a value and pass both of them to the Register component. Also, pass the data to your chat component as you would need it. You can try like below,
In App component,
...
function App(props) {
const [data,setData] = useState('');
const myDataSetterFunction = (dataToBeSet) => {
setData(dataToBeSet);
}
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Register intialData={data} myDataSetterFunction={myDataSetterFunction} />}>
<Route
path="chat"
element={(props) => <Chat dataToBeSupplied={data} {...props} data={true} />}
/>
</Route>
<Route
....
The Register component can modify the data and use the passed setter Function to modify the state of the hook. As soon as the data is modified there will be a re-render triggered which will pass the data or you can pass the data only if the data changes from the initial value to be more safer.
Inside the Register.js use the props to call the function passed to set the data inside the app doing something like this.
props.myDataSetterFunction( ...dataToBeReturned... );
Also, Remember to make sure the data has some value before passing it to the chat and using it.
In here, you can use queryParams when you want to switch to a new component:
navigate({
pathname: '/chat',
search: '?message=hello parent',
});
In React we cannot directly pass the props from the children to the parents. you can pass a function as a prop from the parent component to a child component, then call that function in the child component.
If Register comp took the input name then render Chat comp. Not sure if I needed this react-router-dom thing
function Register() {
const navigate = useNavigate();
const [passedUsername, setForPassedUsername] = useState(false);
const [name, setName] = useState("");
const generatedName = `user#${Math.floor(Math.random() * 1000000 + 1)}`;
const handleSubmit = () => {
if (name === "") {
setName(generatedName);
}
setForPassedUsername(true);
navigate("/chat", { replace: true });
return "hello parent";
};
return (
<div>
{passedUsername ? (
<Chat data={name}></Chat>
) : (
<div>
Enter username:{" "}
<input
placeholder={generatedName}
onChange={(e) => setName(e.target.value)}
></input>
<button onClick={() => handleSubmit()}>enter</button>
</div>
)}
</div>
);
}
Related
Shortly, when I try to use useState with useContext in one of my components, all pages just disappear. UseState in some reason block my Routers and I have no idea why... Can you tell me where is my mistake?
Some code below:
Index.js
export default function App() {
const [value, setValue] = useState(false) -----> here I set the state
return (
<BrowserRouter>
<UserContext.Provider value={{ value, setValue }}>
<Routes>
<Route path='/' element={<Layout />}>
<Route index element={<Home />} />
<Route path='Home' element={<Home />} />
<Route path='Menu' element={<Menu />} />
<Route path='Story' element={<Story />} />
<Route path='Coffee' element={<Coffee />} />
<Route path='Cart' element={<Cart />} />
</Route>
</Routes>
</UserContext.Provider>
</BrowserRouter>
)
}
// ReactDOM.render(<App />, document.getElementById("root"))
const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(<App />)
Buy.js component
import { useState } from "react"
import { useContext } from "react"
import { UserContext } from "../../UserContext"
const Buy = () => {
const [buttonText, setButtonText] = useState("Add to cart")
const [isActive, setIsActive] = useState(false)
// const [value, setValue] = useContext(UserContext) --> after I declare state everything disappears
const addToCart = () => {
setIsActive((current) => !current)
// setValue(true)
if (isActive) {
setButtonText("Add to cart")
}
}
return (
<div>
<button
class='buy'
style={{
fontSize: isActive ? "0.8rem" : "1rem",
color: isActive ? "lightgray" : "white",
}}
onClick={() => {
addToCart()
}}
>
{buttonText}
</button>
</div>
)
}
export default Buy
UserContext.js
import { createContext } from "react"
export const UserContext = createContext(null)
Actually, I need this Context only for routes "Coffee" and "Cart", but if I wrap only this 2 routes will be the same problem and all is disappear. Should I maybe use Context in my Layout.jsx instead of Index.js? Thank you.
const Layout = () => {
return (
<>
<Navbar />
<Outlet />
<Footer/>
</>
);
};
export default Layout;
The errors in console:
Your context provides an object, not an array, so you should destructure using curly braces when you use it:
const { value, setValue } = useContext(UserContext);
Or if you want to keep this way of destructuring, you can provide an array instead:
<UserContext.Provider value={[value, setValue]}>
There are a few issues I see with the React code that I myself struggled with while learning React.
Issue #1
Your button in Buy.js has a class='buy' property. Rename that to className='buy' because that's just React's syntax.
Issue #2
Your button's onClick={} property should reference only the function's name, and should not call the function itself. Change the onClick to onClick={addToCart}. Do not add the anonymous arrow function, simply input the name of the function.
Possible Issue #3
Most of the conditional functionality you are looking for can be implemented with React's useEffect() hook. Change addToCart() in the following way:
const addToCart = () => {
setIsActive();
}
useEffect(() => {
if(isActive) {
setButtonText("Add to cart");
}
}, [isActive]);
Make sure to import useEffect() before using this.
First i cannot see your Buy.js component between Context.Provider tree.
Then please try to use as object to destruct state values, not array.
In my App.js I have a ref:
const canvasView1 = React.createRef();
...
<div ref={canvasView1}/>
And a route to a Homepage component with the ref as a prop:
<Route
index
path="/welcome"
element={<Homepage canvasView1={canvasView1}/>}
/>
Then, in Homepage.js I use forwardRef() and I log the forwarded ref :
export const Homepage = React.forwardRef((props, canvasView1) => {
useEffect(() => {
console.log('# canvasView1 Homepage.js :', canvasView1)
}, [canvasView1]);
...
}
But it returns null whereas in App.js it returns the object:
I've read the docs about refs forwarding and tried multiple syntax but it still doesn't work.
Since you are using React function components you'll want to use the useRef hook so the created ref is a stable reference. React.createRef() will create a brand new React ref reference each render cycle.
const canvasView1 = React.useRef();
The Homepage component is forwarding the special ref prop, not any of the other regular named props that may or may not hold a React ref value. Pass the canvasView1 ref on the Homepage component's ref prop so it's forwarded.
<Homepage ref={canvasView1} />
or update the Homepage component to access the passed canvasView1 prop.
export const Homepage = ({ canvasView1 }) => {
React.useEffect(() => {
console.log("# canvasView1 Homepage.js :", canvasView1);
}, [canvasView1]);
return <h1 ref={canvasView1}>Homepage</h1>;
});
Code:
export default function App() {
const canvasView1 = React.useRef();
React.useEffect(() => {
console.log("# canvasView1 App.js :", canvasView1);
}, [canvasView1]);
return (
<div className="App">
<div ref={canvasView1} />
<Routes>
<Route path="/" element={<Homepage ref={canvasView1} />} />
</Routes>
</div>
);
}
I create a let variable in my parent component with the boolean false. I pass that variable as a prop to the child component. I change the variable to true through a function in the parent component.
My problem is that the value of the prop in the child component is still false when I console.log it afterwards.
Parent:
function App() {
let success = false
const changePW = async ({password, repeatPW}) => {
success = true
console.log(`Success App0: ${success}`)
console.log('ChangePW')
return (
<BrowserRouter>
<div className="container">
<Header/>
<Route path='/' exact render={(props) => (
<>
<AddPasswort onChange = {changePW} success = {success}/>
<Footer />
</>
)}/>
<Route path='/about' component={About} />
<Route path='/success' component={Success} />
<Route path='/error' component={Error} />
</div>
</BrowserRouter>
);
}
export default App;
Child
const AddPasswort = ({onChange,success}) => {
const[password, setPassword] = useState('')
const[repeatPW, setRepeatPW] = useState('')
// create a history object
const history = useHistory()
const onSubmit = async (e) => {
e.preventDefault();
await onChange({password, repeatPW})
console.log(success)
// navigate to the success page
if (success){
console.log('success')
history.push("/success")
}
else{
console.log('error')
history.push("/error")
}
}
}
...
}
export default withRouter(AddPasswort);
I thought the problem was that the function does not wait for onChange to finish so I made it asynch, but that did not resolve the issue
because success is not a state,
only changing state will re-render component.
try
const [success, setSuccess] = useState(false);
to change the value of success to true , do
setState(true)
this should solve your problem
I am not able to send the parameter through state using useHistory history.push method from react-router dom.
Now suppose I want to pass more than a string to the Paging component i.e. some props too.
My Paging Component which throws error for state value state is not defined
const PAGING = ({ location }) => {
console.log(location);
console.log(location.state);
console.log(location.state.id);
return <div>Hello <div>}
History.push method in another component
const handleDetails = (id,name) => {
console.log(name)
if (id) {
return history.push({
pathname: `/detailing/${name}`,
state: { id }
});
} else {
return history.push("/");
}
};
const Switch = () => {
const { state: authState } = useContext(AuthContext)
return (
<div>
<Router>
<Switch>
<ProtectedSystem
path= "/detailing/:name"
exact
auth={authState.isAuthenticated}
component={PAGING}
/>
</Switch>
</Router>
</div>
);
const ProtectedSystem = ({auth , component: Component, ...rest}) =>{
return(
<Route
{...rest}
render={() => auth ? (<Component/>) : (<Redirect to = '/' /> )}
/>
)
}
If I use simple route without condition based its working fine
<Route path= "/detailing/:name" exact component={PAGING} />
You need to pass on the Route params to the rendered component so that it can use them
const ProtectedSystem = ({auth , component: Component, ...rest}) =>{
return(
<Route
{...rest}
render={(routeParams) => auth ? (<Component {...routeParams}/>) : (<Redirect to = '/' /> )}
/>
)
}
You can do this entirely with React hooks and pure functions, eg.
import React from 'react';
import { useHistory } from 'react-router-dom';
const ProtectedSystem = ({ auth }) => {
const history = useHistory();
if (!authUser) {
history.push("/signin");
}
return (
<div><h1>Authorized user</h1></div>
)
}
export default ProtectedSystem
I'm having trouble accessing my updated global state in react via hooks. I created custom hooks to get away from reducers and anything redux because I'm not a fan of it. Everything is working well but i cannot access my state.
Here is how the global state is set up
import React, {useState, useMemo, useContext} from 'react'
function makeStore() {
// Make a context for the store
const Context = React.createContext();
// Make a provider that takes an initialValue
const Provider = ({ initialValue, children }) => {
// Make a new state instance (could even use immer here!)
const [state, setState] = useState(initialValue);
// Memoize the context value to update when the state does
const contextValue = useMemo(() => [state, setState], [state]);
// Provide the store to children
return <Context.Provider value={contextValue}>{children}</Context.Provider>;
};
// A hook to help consume the store
const useStore = () => useContext(Context);
return { Provider, useStore };
}
const {Provider, useStore} = makeStore();
export {
Provider,
useStore
}
I make sure to wrap the store provider around the App component
ReactDOM.render(
<Router basename="">
<Provider initialValue={initialState} >
<App />
</Provider>
</Router>,
document.getElementById("root")
);
The App component is clean and mainly used for routes
const App = () =>
<div className="App">
<Route exact path="/" component={props => <Landing {...props} />} />
<Route exact path="/login" component={props => <Login {...props} />} />
<Route exact path="/register/:id" component={props => <Register {...props} />}/>
<PrivateRoute path='/:id' Component={<Content />} />
</div>
export default App
When I hit the login page, enter login info, and click submit, the state updates. The only thing is I can't access the updated state inside my component.
Heres the login component
const Login = ({...props}) => {
const { register, handleSubmit } = useForm()
const {auth, setAuth} = useAuth()
const {loginUser} = AuthApi()
const onSubmit = data => (async () => {
const response = await loginUser(data)
setAuth(response, true)
})()
useEffect(() => {
if(auth.isAuthenticated === true)
props.history.push('/dashboard')
}, [])
return(
<div className="Auth" style={{backgroundImage: 'url(' + background + ')'}}>
<div className="Login">
<div className="Auth-Form Login-Form">
<div className="Form-Header">
<h4>
<b>Login</b>
</h4>
</div>
<div className="Form">
<form onSubmit={handleSubmit(onSubmit)}>
<input
placeholder="Email"
name="email"
ref={register({
required: "email required"
})}
/>
<br/>
<input
placeholder="Password"
name="password"
ref={
register({
required: "password required"
})
}
/>
<br />
<input id="submit" type="submit" placeholder="Create Profile"/>
</form>
<p className="">
Don't have an account? <Link to="/register/user">Register</Link>
</p>
<Link to="/" className="">Back to home</Link>
</div>
</div>
</div>
</div>
)
}
export default Login;
and here's the useAuth hook code
const useAuth = () => {
const [{...auth}, setState] = useStore()
const setAuth = (data, authenticationStatus) => {
setState(state => {
state.auth.token = data.token
state.auth.user = data.user
state.auth.loading = true
state.auth.isAuthenticated = authenticationStatus
})
}
return {auth, setAuth}
}
when the value of auth.isAutheticated coverts to true in the login component, nothing happens. In the provider the state is updated and even in the useAuth hook I console log the current state and its updated. Yet I can't access it in the login component despite using useEffects? is there something I'm missing.
this code only happens like componentdidmount - once:
useEffect(() => {
if(auth.isAuthenticated === true)
props.history.push('/dashboard')
}, [])
change to
useEffect(() => {
if(auth.isAuthenticated === true)
props.history.push('/dashboard')
})