update state of child component from parent component using react hooks - reactjs

This is how my RespMessages component looks like:
import React, { useState } from "react";
import { Message } from "semantic-ui-react";
function RespMessages() {
const [message, setMessage] = useState("This is a success message");
return (
<Message positive>
<Message.Header>{message}</Message.Header>
</Message>
);
}
export default RespMessages;
This is how I am using RespMessages component inside a different component.
function CreateChannel() {
return (
<Container>
<RespMessages />
</Container>
}
This works fine and I can see This is a success message when the page renders.
What I am not able to figure out is how do I call setMessage of RespMessages component from CreateChannel component.
Can you please help?
P.S.: I am react newbie so jargons of react are probably off.

You can do it using useImperativeHandle. Here is an example.
let RespMessages = forwardRef((props, ref) => {
const [message, setMessage] = useState("This is a success message");
const inputRef = useRef();
useImperativeHandle(ref, () => ({
setMessage: (msg) => setMessage(msg)
}));
return <div>{message}</div>;
});
function App() {
// In order to gain access to the child component instance,
// you need to assign it to a `ref`, so we call `useRef()` to get one
const childRef = useRef();
return (
<div>
<RespMessages ref={childRef} />
<button onClick={() => childRef.current.setMessage('hey')}>Click</button>
</div>
);
}
As stated in the docs, this approach should be rarely used. Also consider approaches in other answers.

In react if you want something to change in the Child component depending on the Parent component you have to lift the state up to the parent component. If you want to differentiate between what should be state and what should be props and where should the state reside then its simple to figure out.
State: Keep the state (message) in the component where the data will change. In this case CreateChannel component.
Props: The components that are dependent on that state (message) should get the state as Props. In this case RespMessages component.
setState Methods: Now in case the child also wants to be able to set the state (message) then you can also pass the setMessage down as prop from CreateChannel to RespMessages`.
The react docs have a very good explanation for all this stuff. Please check it out if needed.
RespMessages.jsx
import React from "react";
import { Message } from "semantic-ui-react";
function RespMessages({message}) {
return (
<Message positive>
<Message.Header>{message}</Message.Header>
</Message>
);
}
export default RespMessages;
CreateChannel.jsx
import React, { useState } from "react"
import { Container } from "semantic-ui-react"
function CreateChannel() {
const [message, setMessage] = useState("")
return (
<Container>
<RespMessages message={message} />
</Container>
)
}
export default CreateChannel

You don't. A parent company generally should not know about the implementation of a child.
You can move your messages state into parent and pass the messages to child as props. This will change your RespMessage component from being an uncontrolled component, to a controlled component.

Related

Is there a reason to not import/export state in place of prop drilling or useContext in react?

Is it possible to do something like so?
MyComponent.jsx :
let myState;
let setMyState;
const MyComponent = () => {
const [myState, setMyState] = useState(35);
return (
<div> Some Jsx </div>
)
}
export myState
export setMyState
ComponentToShareState.jsx :
import myState from MyComponent.jsx
import setMyState from MyComponent.jsx
const MyComponentToShareState = () => {
const onClick = () => {
setMyState(100);
}
return (
<div>
<button onClick=(onClick)> Change my State </button>
<p>{myState}</p>
</div>)
}
Basically is there a reason that prop drilling exists rather than just exporting and importing state from component to component? Is it just for cleanliness of unidirectional flow? Would is actually break the app? Can you explain why this is not a used practice to me?

Pass the data from child functional component to parent class component

I am quite new to react and I have been trying to figure out how to pass the data from child function to parent class component. In my child, I only have a variable that stores username from the URL. But I can't seem to figure out how to pass it to the parent component.
Here is my child.
const GetUsername = () => {
const params = useParams();
const [param, getParam] = useState(params.name);
return <p>{params.name}</p>;
};
export blabla
I would like to know how I would have to access that params.name in my parent component.
Here is my parent.
export class test extends Component {
constructor(props) {
super(props);
this.data = React.createRef();
}
render() {
console.log(this.data);
return ({some code});
}
When I ran this, I got null. Thank you so much in advance for helping me!
In React, Data flows in one direction only in form of props from Parent to Child.
Either move the logic to fetch params name to parent and pass it to child as props or have a central data storage such as redux and dispatch an action from child to save the information there and fetch it from there in Parent.
If you want to pass a child to parent component data then use usecontext or use redux to pass the data I recommend you can use redux.
parent component is handling the state
import GetUsername from "./GetUserName"
import { useState } from 'react'
const Test = () => {
const [param, getParam] = useState('');
console.log(param)
return (
<div>
<GetUsername getParam={getParam}/>
</div>
)
}
export default Test
and the children GetUserName utilizes the getParam useState setter to set the state handled in its parent component Test
import { useEffect } from "react";
import { useParams } from "react-router-dom";
const GetUsername = ({getParam}) => {
const params = useParams();
useEffect(()=>{
getParam(params)
},[])
return <p>children component</p>;
};
export default GetUsername
you will see that the console.log in the Test component will output your param received in its child component GetUserName.
in case you don't want to pass the props inline, you can use useContext, or redux, or any other state management library like zustand, recoil, etc...
You can pass the setState hook as a prop from the parent to the child and when you have access to the data. Use the setState hook.
Parent Component:
export default function App() {
const [name, setName] = useState();
return (
<div className="App">
<h1>Hello {name}</h1>
<Child setName={setName} />
</div>
);
}
Child Component:
export default function Child({ setName }) {
const [param, getParam] = useState("blabla");
useEffect(() => {
setName(param);
}, []);
return (
<>
<input onChange={(e) => setName(e.target.value)} />
</>
);
}

Transform Function Component to a Class Component

I am still learning React and I am wondering if it is possible to make the function component below into a class component.
Component:
import React, { useState } from "react"
import { useStaticQuery, graphql } from "gatsby"
import Navbar from "./navbar"
const Layout = ({ location, title, children }) => {
const rootPath = `${__PATH_PREFIX__}/`
const [classNames, setClassNames] = useState('')
const updateClasses = (classNames) => {
setClassNames(classNames)
}
const data = useStaticQuery(graphql`
{
site {
siteMetadata {
menuLinks {
link
name
}
}
}
}
`)
return (
<div>
<Navbar pages={ data.site.siteMetadata.menuLinks } updateClassNames={updateClasses} />
<main className={classNames}>{children}</main>
</div>
)
}
export default Layout
My biggest issue is with the parameters that are passed to the function location, title, children. What will happen with them. I am not using them at the moment, but will need them later.
Class or function component is not much different except using hooks.
With your current function component, just use the location, title, etc props like other variables in a normal function.
Why do you need to convert into class component?
You don't need a class component to lift up a class, value, or any data in a child component, it's the same behavior rather than class-based component or functional component. You just need to pass via props a function that it will be triggered in a child component to lift up some data again to the parent.
In your parent component, you need to set the function. Without knowing its structure, it would look like:
someFunction= value => {
console.log('I have the value: ', value)
}
return <Layout someFunction={someFunction}
Disclaimer: you may need to adapt the code to your component. The idea is to set a function and pass it via props in the return.
Then, in your <Layout> component, you can destructure the function as you do with location, title and children, and trigger when you need it:
import React, { useState } from "react"
import { useStaticQuery, graphql } from "gatsby"
import Navbar from "./navbar"
const Layout = ({ location, title, children, someFunction }) => {
const rootPath = `${__PATH_PREFIX__}/`
const [classNames, setClassNames] = useState('')
const updateClasses = (classNames) => {
setClassNames(classNames)
}
const handleClick=()=>{
someFunction('hello')
}
const data = useStaticQuery(graphql`
{
site {
siteMetadata {
menuLinks {
link
name
}
}
}
}
`)
return (
<div>
<Navbar pages={ data.site.siteMetadata.menuLinks } updateClassNames={updateClasses} onClick={()=>handleClick} />
<main className={classNames}>{children}</main>
</div>
)
}
export default Layout
In this dummy example, you will be passing 'hello' to the parent component when the <Navbar> is clicked, of course, you can pass any desired value or use a useEffect hook or whatever you need. This is the way to pass data from child to parent component.

How would I use React Hooks to replace my withAuth() HOC?

I've been spending a bunch of time reading up on React Hooks, and while the functionality seems more intuitive, readable, and concise than using classes with local state and lifecycle methods, I keep reading references to Hooks being a replacement for HOCs.
The primary HOC I have used in React apps is withAuth -- basically a function that checks to see if the currentUser (stored in Redux state) is authenticated, and if so, to render the wrapped component.
Here is an implementation of this:
import React, { Component } from "react";
import { connect } from "react-redux";
export default function withAuth(ComponentToBeRendered) {
class Authenticate extends Component {
componentWillMount() {
if (this.props.isAuthenticated === false) {
this.props.history.push("/signin");
}
}
componentWillUpdate(nextProps) {
if (nextProps.isAuthenticated === false) {
this.props.history.push("/signin");
}
}
render() {
return <ComponentToBeRendered {...this.props} />;
}
}
function mapStateToProps(state) {
return { isAuthenticated: state.currentUser.isAuthenticated };
}
return connect(mapStateToProps)(Authenticate);
}
What I can't see is how I can replace this HOC with hooks, especially since hooks don't run until after the render method is called. That means I would not be able to use a hook on what would have formerly been ProtectedComponent (wrapped with withAuth) to determine whether to render it or not since it would already be rendered.
What is the new fancy hook way to handle this type of scenario?
render()
We can reframe the question of 'to render or not to render' a tiny bit. The render method will always be called before either hook-based callbacks or lifecycle methods. This holds except for some soon-to-be deprecated lifecycle methods.
So instead, your render method (or functional component) has to handle all its possible states, including states that require nothing be rendered. Either that, or the job of rendering nothing can be lifted up to a parent component. It's the difference between:
const Child = (props) => props.yes && <div>Hi</div>;
// ...
<Parent>
<Child yes={props.childYes} />
</Parent>
and
const Child = (props) => <div>Hi</div>;
// ...
<Parent>
{props.childYes && <Child />}
</Parent>
Deciding which one of these to use is situational.
Hooks
There are ways of using hooks to solve the same problems the HOCs do. I'd start with what the HOC offers; a way of accessing user data on the application state, and redirecting to /signin when the data signifies an invalid session. We can provide both of those things with hooks.
import { useSelector } from "react-redux";
const mapState = state => ({
isAuthenticated: state.currentUser.isAuthenticated
});
const MySecurePage = props => {
const { isAuthenticated } = useSelector(mapState);
useEffect(
() => {
if (!isAuthenticated) {
history.push("/signin");
}
},
[isAuthenticated]
);
return isAuthenticated && <MyPage {...props} />;
};
A couple of things happening in the example above. We're using the useSelector hook from react-redux to access the the state just as we were previously doing using connect, only with much less code.
We're also using the value we get from useSelector to conditionally fire a side effect with the useEffect hook. By default the callback we pass to useEffect is called after each render. But here we also pass an array of the dependencies, which tells React we only want the effect to fire when a dependency changes (in addition to the first render, which always fires the effect). Thus we will be redirected when isAuthenticated starts out false, or becomes false.
While this example used a component definition, this works as a custom hook as well:
const mapState = state => ({
isAuthenticated: state.currentUser.isAuthenticated
});
const useAuth = () => {
const { isAuthenticated } = useSelector(mapState);
useEffect(
() => {
if (!isAuthenticated) {
history.push("/signin");
}
},
[isAuthenticated]
);
return isAuthenticated;
};
const MySecurePage = (props) => {
return useAuth() && <MyPage {...props} />;
};
One last thing - you might wonder about doing something like this:
const AuthWrapper = (props) => useAuth() && props.children;
in order to be able to do things like this:
<AuthWrapper>
<Sensitive />
<View />
<Elements />
</AuthWrapper>
You may well decide this last example is the approach for you, but I would read this before deciding.
Building on the answer provided by backtick, this chunk of code should do what you're looking for:
import React, { useEffect } from "react";
import { useSelector } from "react-redux";
const withAuth = (ComponentToBeRendered) => {
const mapState = (state) => ({
isAuthenticated: state.currentUser.isAuthenticated,
});
const Authenticate = (props) => {
const { isAuthenticated } = useSelector(mapState);
useEffect(() => {
if (!isAuthenticated) {
props.history.push("/signin");
}
}, [isAuthenticated]);
return isAuthenticated && <ComponentToBeRendered {...props} />;
};
return Authenticate;
};
export default withAuth;
You could render this in a container using React-Router-DOM as such:
import withAuth from "../hocs/withAuth"
import Component from "../components/Component"
// ...
<Route path='...' component={withAuth(Component)} />

How can I get props in this case?

I'm learning HOC, I have a question how is it possible to get props in HOC in this case.
withRainbow.js(HOC)
import React from 'react';
const withRainbow = (WrappedComponent) => {
const colors = ['red', 'blue', 'orange'];
const randomColor = colors[Math.floor(Math.random() * 3)];
const className = randomColor + '-text';
return ( props ) => {
return (
<div className={className}>
<WrappedComponent {...props}/>
</div>
)
};
}
export default withRainbow;
About.jsx
import React from 'react';
import withRainbow from '../hoc/withRainbox';
const About = () => {
return (
<div className="container">
<h4 className="center">About</h4>
<p>This is about yay!</p>
</div>
);
}
export default withRainbow(About);
How is it possible to get props through callback return (props) => ... in withRainbow.js even though withRainbow(About) in About.jsx has no argument props?
If About.jsx has state, can I get it too in withRainbow.js?
How is it possible to get props through callback return (props) => ...
in withRainbow.js even though withRainbow(About) in About.jsx has no
argument props?
withRainbow(About) returns a new component to which if you pass props while rendering, it can access props like you do in the withRainbow HOC event though you aren't access props in the About component.
const AboutWithRainbow = withRainbow(About);
...
return (
<AboutWithRainbow abc={'2433'} />
)
If About.jsx has state, can I get it too in withRainbow.js?
No you shouldn't access state of a child component in parent. If these is such a case, you must lift the state up
There is a confusion because you are looking at it in the wrong direction.
The props that the function receives are not from child component that you are exporting as withRainbow(About).
It is coming from the parent component that is calling withRainbow(About)
// In about component.
const AboutWithRainbow = withRainbow(About);
// In parent component
<AboutWithRainbow {...propsFromParent} />
These are the props being received in the HOC component.
This also answers the second question. No, you cannot get state as an argument because data flows down.

Resources