I am trying to do:
Change child state as in this tutorial: Change child state from parent
Tutorial uses class component, I've tried to use function components.It gives an error like "Function components don't have instances so they can't have refs"
So I used forwardRef (for the first time) according to a stackoverflow answer here
Code:
import React,{useRef,forwardRef, useEffect} from "react";
const Component = (props) => {
function sayHello(){
console.log("hello")
}
return <h1>{props.children}</h1>
}
const ForwardComponent = forwardRef((props,ref)=> (<Component ref={ref}> {props.children} </Component>))
export default function App() {
const hello = useRef()
useEffect(()=>{
hello.current.sayHello()
})
return (
<div>
<ForwardComponent ref={hello}>Hello</ForwardComponent>
</div>
);
}
note: I could use context api or redux here, but I believe it should be simpler in my use case. Also I get to learn something new
Related
I want to make a ref context, using that context's refs in my sections and then consuming the context in my navbar to use intersectionObserver.
Trying to achieve this at first i created a context with every ref and then using the context in my about component.
The context looks like this:
import React, { createContext, useRef } from "react";
export const RefsContext = createContext();
export const RefsProvider = (props) => {
const aboutRef = useRef();
const shopRef = useRef();
const customersRef = useRef();
return (
<RefsContext.Provider
value={{
aboutRef,
shopRef,
customersRef,
}}
>
{props.children}
</RefsContext.Provider>
);
};
export default RefsProvider;
And this is how i consume that context in my about.js:
First i import the context: import { RefsContext } from "../../../context/refsContext"; then i use it: const { aboutRef } = useContext(RefsContext); and here i use that ref to referenciate a section: <section className="about" id="about" ref={aboutRef}>
I get this error message Uncaught TypeError: Cannot destructure property 'aboutRef' of '(0 , react__WEBPACK_IMPORTED_MODULE_0__.useContext)(...)' as it is undefined. in about.js:10, this line: const { aboutRef } = useContext(RefsContext);
Looks like you forgot to wrap whatever component is using this in RefsProvider. Does that fix this problem?
EDIT: To provide further context, if you use useContext inside a component that is not wrapped in the Provider for the context you are accessing, the value will be undefined. I suspect that is what is happening here.
In order to fix this, you need to wrap the component in RefsProvider
For example:
<RefsProvider>
<YourComponentThatUsesRefsContext />
</RefsProvider>
I have defined a context using createContext that wraps some of my components like
import MyContext from './mycontext.js'
import A from 'a.js';
import B from b.js';
<MyContext>
<A/>
<B/>
</MyContext>
where A is defined something like
import C from './c.js'
const A = () => {
return (<C/>);
}
And C is define something like
import MyContext from 'mycontext.js';
const C = () => {
const { value, setValue } = useContext(MyContext);
return (<div>`This is the value - ${!!value ? value : 'UNK'}`</div>)
}
Finally the context is created like
const MyContext = createContext({value: '', setValue: () => {}});
The problem is that I get a runtime error
TypeError: Object is not iterable (cannot read property Symbol(Symbol.iterator))
From the component C.
I want to make provision for calling C outside of the provider. Not as a wrapped child of the provider. Where does this error come from and how do I work around it?
From only what I can tell from the code snippets it appears you're not rendering any context provider and not providing a context value.
Instead of trying to use the MyContext context as a React component, render the Provider component.
<MyContext.Provider value={{ value: 42, setValue: console.log }}>
<A />
<B />
</MyContext.Provider>
I'm new to react development. I often came accross code examples where a component takes other component as props, like so:
import AccountCircleIcon from '#material-ui/icons/AccountCircle';
import { UserInfo } from './userinfo';
const UserPanel = (props) => {
return (
<UserInfo icon={<AccountCircleIcon/>} user={props.user}>
{props.children}
</UserInfo>
);
}
Other time, I see component that takes class name as one of its props. Like so:
import { Admin } from 'react-admin';
import { AppLayout } from './components';
const App = (props) => {
return (
<Admin layout={AppLayout}>
{* other codes here ... *}
</Admin>
);
}
What's the difference? If both approach can be used to achieve the same effect, which one is considered better?
The difference is that on the first code, you're calling the render function on your component and passing the result render as a prop. On the second code, you're sending the component (not rendered yet!) as a prop and will use it to render in another place of your tree.
Essentially they're the same and it's up to how you want to drive passing props on your project.
Just to illustrate, consider that you have two components:
// Returns a JSX.Element
function AccountCircleIcon() {
return (<>Foo</>)
}
// Also, returns a JSX.Element
function AppLayout() {
return (<>Bar</>)
}
function App() {
return (
<>
// The two lines below will produce exactly a JSX.Element
<AccountCircleIcon/>
{AppLayout()}
// Dependency prop here will be a valid React component
// Like a function you can call: AppLayout()
<AnotherComponent dependency={AppLayout}/>
// Dependency prop will be also valid React component
// but it's already rendered.
<AnotherComponent dependency={<AccountCircleIcon/>}/>
</>
)
}
Check this out to understand and play a little bit with the idea.
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.
I see in some source code, the author wrote a component like this:
import React from 'react';
export const Login = () => (
<div>
<h4>Hello World</h4>
</div>
);
export default Login;
The thing I don't know is:
How react understand this is a react component by only using import
How can I add another callback method, such as viewDidMount ... I have added in code block but it compiles fail.
thanks
This is functional stateless component. It is for simple components.
You also can add component property types like this:
export const Login = () => (
<div>
<h4>Hello World</h4>
</div>
);
Login.propTypes = {
username: React.PropTypes.string.isRequired,
}
You can add callback like this:
// this place to define callback is more effective
const onClick = e => (...)
const Login = props => {
// or define here if you need access to props in onClick
const onClick = e => (...)
return <button onClick={onClick}>Submit</button>
}
React "knows" that the code you wrote is a React Component because of transpilation. Transpilation is a process that occurs during build time where your code is changed from the code you wrote into something else.
In the case of React and JSX, your code turns into
export const Login = () => (
React.createElement('div', {},
React.createElement('h4', {}, 'Hello World')
);
);
The angle brackets (<) are syntactic sugar for React.createElement and people use the angle brackets because they are simpler to use and type.