Starting from the problem
I've been asked a question like this:
Want to achieve oneuseTitle
Methods, specific usage examples are as follows:
function Header() { const [Title, changeTitle] = useTitle(); return ( <div onClick={() => changeTitle('new title')}> <Title /> </div> ) }
But writinguseTitle
There was a problem with the code:
function TitleComponent({title}) { return <div>{title}</div> } function useTitle() { const [title, changeTitle] = useState('default title'); useEffect(() => { changeTitle(title) }, [title]) const Element = (TitleComponent, {title}); return [, changeTitle]; }
This code directly reports an error and cannot even render it. If it were you, how should you modify this code?
Elements and Components
In fact, this is a very typical question of how to distinguish and use elements and components.
element
Let's look at React firstIntroduction to React elements in official documentation:
Babel will translate JSX into a()
Function call. The following two example codes are completely equivalent:
const element = <h1 className="greeting">Hello, world!</h1>; const element = ( 'h1', {className: 'greeting'}, 'Hello, world!' );
()
Some checks will be performed in advance to help you write error-free code, but in fact it creates an object like this:
// Note: This is a simplified structureconst element = { type: 'h1', props: { className: 'greeting', children: 'Hello, world!' } };
These objects are called "React elements". They describe what you want to see on the screen.
You see, the React element actually refers to the JSX code we write every day. It will be escaped by Babel as a function call. The final result is an object describing the DOM structure, and its data structure is essentially a JS object.
In JSX, we can embed expressions, such as:
const name = 'Josh Perez'; const element = <h1>Hello, {name}</h1>;
So if we want to use a React element, we should use embedded expressions:
const name = <span>Josh Perez</span>; const element = <h1>Hello, {name}</h1>;
Components
What about the components? There are two types of components, function components and class components:
// Function componentsfunction Welcome(props) { return <h1>Hello, {}</h1>; } // class componentclass Welcome extends { render() { return <h1>Hello, {}</h1>; } }
So how do you use components?
const element = <Welcome name="Sara" />;
For components, we want to call them in a way similar to HTML tags, and Babel will translate them into a function call
const element = (Welcome, { name: "Sara" });
So you see, the data structure of a component is essentially a function or class. When you call it using the element label, the function or class will be executed and eventually return a React element.
How to solve the problem
Although these contents come from the official React documentation, if you can clearly understand the differences between React elements and components, you can already solve the problem at the beginning. There are at least two ways to solve it, one is to return the React element and the other is to return the React component
The first one we return the React element:
const root = (('root')); function Header() { const [Title, changeTitle] = useTitle(); // Because the React element is returned here, we use {} to embed the expression return ( <div onClick={() => changeTitle('new title')}> {Title} </div> ) } function TitleComponent({title}) { return <div>{title}</div> } function useTitle() { const [title, changeTitle] = useState('default title'); useEffect(() => { changeTitle(title) }, [title]) // createElement returns React element const Element = (TitleComponent, {title}); return [Element, changeTitle]; } (<Header />);
The second type we return to the React component:
const root = (('root')); function Header() { const [Title, changeTitle] = useTitle(); // Because the return is a React component, we call it using the element tag return ( <div onClick={() => changeTitle('new title')}> <Title /> </div> ) } function TitleComponent({title}) { return <div>{title}</div> } function useTitle() { const [title, changeTitle] = useState('default title'); useEffect(() => { changeTitle(title) }, [title]) // Here we build a function component const returnComponent = () => { return <TitleComponent title={title} /> } // Here we directly return the component return [returnComponent, changeTitle]; } (<Header />);
Custom content
Sometimes we need to pass a custom content to the component.
For example, we implemented a Modal component, which has a definite button and a cancel button. However, in order to be more flexible, we provide a props property where the user can customize a component to pass into it. What the user provides, Modal will show what the Modal is equivalent to a container. So, how should we implement this function?
The first way to implement
The following is the first implementation method:
function Modal({content}) { return ( <div> {content} <button>Sure</button> <button>Cancel</button> </div> ) } function CustomContent({text}) { return <div>{text}</div> } <Modal content={<CustomContent text="content" />} />
Based on the previous knowledge, we can know thatcontent
The property is actually a React element, so the Modal component is used inside it.{}
Make rendering.
The second way to implement it
But the first method does not always solve the needs. Sometimes, we may use the values inside the component.
For example, a countdown componentTimer
, still provides a propertycontent
, a display style for custom time, time byTimer
The component is processed internally, and the display style is completely customized by the user. In this case, we can choose to pass in a component:
function Timer({content: Content}) { const [time, changeTime] = useState('0'); useEffect(() => { setTimeout(() => { changeTime((new Date).toLocaleTimeString()) }, 1000) }, [time]) return ( <div> <Content time={time} /> </div> ) } function CustomContent({time}) { return <div style={{border: '1px solid #ccc'}}>{time}</div> } <Timer content={CustomContent} />
In this example, we can seecontent
The property passes in a React component CustomContent, and the CustomContent component will be passed in the time property. We are developing the CustomContent component based on this convention.
Inside the Timer component, because the component is passed in, it is used.<Content time={time}/>
rendering.
The third way to implement it
When facing the needs of the second implementation method, in addition to the above implementation method, there is another one calledrender props
The trick is more common than the second method. Let's still take the Timer component as an example:
function Timer({renderContent}) { const [time, changeTime] = useState('0'); useEffect(() => { setTimeout(() => { changeTime((new Date).toLocaleTimeString()) }, 1000) }, [time]) // The renderContent function passed in is called directly here return ( <div> {renderContent(time)} </div> ) } function CustomContent({time}) { return <div style={{border: '1px solid #ccc'}}>{time}</div> } (<Timer renderContent={(time) => { return <CustomContent time={time} /> }} />);
Given that what we pass in is a function, wecontent
Change the attribute name torenderContent
, in fact, it can be called anything.
renderContent
A function is passed in, which receivestime
As an argument, a React element is returned, and inTimer
Internally, we directly execute the renderContent function and pass in the internally processed time parameters, thereby realizing that users can customize rendering content using the internal values of the component.
To put it more, in addition to put it in the attributes, we can also put it in children, it is the same:
function Timer({children}) { // ... return ( <div> {children(time)} </div> ) } <Timer> {(time) => { return <CustomContent time={time} /> }} </Timer>
We can choose the appropriate incoming method depending on the situation.
React Series
React's createElement source code interpretation
The above is a detailed explanation of the examples of the difference between React elements and components. For more information on the differences between React elements, please pay attention to my other related articles!