Inula
组件
组件是构建用户界面(UI)的基础,是可重用和独立的代码单元。开发者可以将相关功能、状态以及模板封装在组件中,提高可复用性,使得程序开发具备模块化和可维护特性。
Component
功能介绍
Component 是类组件的基础形式,可以通过继承 Component 组件来定义一个类组件,并且使用类的特性,如构造函数、生命周期以及内部状态。
组件定义
class Component<P, S, C> {
props: P;
context: C;
state: S | null;
refs: any;
forceUpdate: any;
isReactComponent: boolean;
constructor(props: P, context: C);
setState(state: S, callback?: Callback): void;
}
props:组件接收的输入属性;context:组件的上下文对象;state:组件内部的状态;refs:用于引用组件中的 DOM 元素或其他组件;forceUpdate:用于强制重新渲染组件;isReactComponent:用于标识当前类是否是 React 组件类;constructor(props: p, context: c):构造函数用于初始化组件的状态和属性;setState(state: S, callback?: Callback): void:用于更新组件的状态,并触发重新渲染。
示例
import * as Inula from 'openinula';
class Counter extends Inula.Component {
constructor(props) {
super(props);
this.state = {
counter: 0
};
console.log('Constructor');
}
componentDidMount() {
console.log('Component Did Mount');
}
componentDidUpdate() {
console.log('Component Did Update');
}
componentWillUnmount() {
console.log('Component Will Unmount');
}
handleIncrement = () => {
this.setState(prevState => ({
counter: prevState.counter + 1
}));
};
render() {
console.log('Render');
return (
<div>
<h1>Counter: {this.state.counter}</h1>
<button onClick={this.handleIncrement}>Increment</button>
</div>
);
}
}
在上述示例中,基于 Component 组件类创建了一个 Component 组件以实现计数器功能,并在组件内部测试了一些声明周期的钩子函数。
PureComponent
功能介绍
PureComponent 类继承自 Component,但是它内置了 shouldComponentUpdate 方法,会自动比较 props 和 state 的变化,如果没有变化则不会重新渲染组件,从而提高性能。
组件定义
class PureComponent<P, S, C> extends Component<P, S, C> {
constructor(props: P, context: C);
}
示例
import Inula, { PureComponent } from 'openinula';
// 创建一个继承自PureComponent的组件
class App extends PureComponent {
render() {
console.log('Render PureDemo');
return <div>{this.props.value}</div>;
}
}
上述示例中,基于 PureComponent 类创建了一个 App 组件,其使用上与 Component 类组件类似,但是其内置了 shouldComponentUpdate 方法,可以直接使用。
提示:在实际开发中,可以根据其优缺点,选择使用
Component和PureComponent两种常用组件类
| 组件 | 优点 | 缺点 |
|---|---|---|
PureComponent | 1.自带浅比较,提高性能 2.适用于大多数情况 | 1.复杂的数据结构可能产生错误或者性能问题 2.对简单组件来说内置方法可能会存在额外开销 |
Component | 1.较为灵活,没有内置方法,可以细粒度控制 2.适用于多控制场景 | 需要手动编写 shouldComponentUpdate 方法 |
memo
功能介绍
memo 是一个高阶组件(HOC),用于封装函数组件,以使其仅在其 props 发生更改时重新渲染。
组件定义
const MemoizedComponent = memo(type, compare?: Function);
type:表示需要被优化的函数式组件;compare:可选参数,用于比较前后两次渲染的属性和状态是否相同的比较函数。如果未提供该函数,默认使用浅层比较。
示例
import { memo, useState } from 'openinula';
const ExpensiveComponent = ({ value }) => {
// 模拟一个昂贵的计算
const expensiveCalculation = () => {
console.log("Calculating...");
let result = 0;
for (let i = 0; i < value * 1000000; i++) {
result += i;
}
return result;
};
const result = expensiveCalculation();
return <div>Result: {result}</div>;
};
const MemoizedExpensiveComponent = memo(ExpensiveComponent);
function App() {
const [count1, setCount1] = useState(1);
const [count2, setCount2] = useState(1);
return (
<div>
<button onClick={() => setCount1(count1 + 1)}>Increment count1</button>
<button onClick={() => setCount2(count2 + 1)}>Increment count2</button>
<MemoizedExpensiveComponent value={count1} />
</div>
);
}
在上述示例中,<ExpensiveComponent> 是一个昂贵的计算组件,它在每次渲染时都会执行昂贵的计算操作。通过使用 memo,可以对 <ExpensiveComponent> 进行优化,避免在 count1 没有变化时重新渲染。memo 会对前后两次渲染的属性和状态进行浅层比较,如果相同则不会重新渲染。
Fragment
功能介绍
<Fragment> 是一个虚拟组件,用于包裹一组元素而不添加额外的 DOM 元素,可以提高渲染性能并减少不必要的包装元素。它可以用来解决在渲染多个元素时产生的包裹元素问题。在一些情况下,如果你不使用 <Fragment>,可能会出现额外的 DOM 层次结构,从而可能影响性能和样式。
示例
import Inula, { Fragment } from 'openinula';
function App() {
return (
<div>
{/* 使用 Fragment */}
<Fragment>
<div>Element 4</div>
<div>Element 5</div>
<div>Element 6</div>
</Fragment>
{/* 或者使用简化形式,使用空尖括号 */}
<>
<div>Element 7</div>
<div>Element 8</div>
<div>Element 9</div>
</>
</div>
);
}
在上面的示例中,你可以看到 <Fragment> 的用法。当需要在组件中渲染多个元素时,使用 <Fragment> 可以避免添加额外的 DOM 元素。除了使用完整的 <Fragment> 标签外,你还可以使用空尖括号的简化形式 <> 和 </>。
Children
功能介绍
Children 用于处理组件的子元素。它允许开发者在组件中访问和操作传递给组件的子元素,无论子元素是什么类型(单个元素、多个元素、文本等)。Children 提供了一些方法来遍历、映射和操作子元素,使得操作子元素变得更加方便。
示例
import Inula, { Children, cloneElement } from 'openinula';
// 示例 1: 遍历子元素并输出它们的类型
function ElementList({ children }) {
const childArray = Children.toArray(children);
const childTypes = childArray.map((child, index) => (
<p key={index}>Child {index + 1} is of type: {child.type.name}</p>
));
return <div>{childTypes}</div>;
}
// 示例 2: 映射子元素并添加额外的 props
function AddPropsToChildren({ children }) {
const enhancedChildren = Children.map(children, (child) => {
return cloneElement(child, { isHighlighted: true });
});
return <div>{enhancedChildren}</div>;
}
// 示例 3: 只渲染特定类型的子元素
function OnlyRenderCertainChildren({ children }) {
const filteredChildren = Children.toArray(children).filter(child => {
return child.type === 'p';
});
return <div>{filteredChildren}</div>;
}
function App() {
return (
<div>
<ElementList>
<h1>Heading</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
<div>Div element</div>
</ElementList>
<AddPropsToChildren>
<span>Child 1</span>
<span>Child 2</span>
</AddPropsToChildren>
<OnlyRenderCertainChildren>
<p>Paragraph 1</p>
<div>Div element</div>
<p>Paragraph 2</p>
</OnlyRenderCertainChildren>
</div>
);
}
上述展示了的示例包含以下方法:
Children.map(children, fn, thisArg?)Children.forEach(children, fn, thisArg?)Children.only(children)Children.toArray(children)。
在上面的示例中,展示了如何使用 Children 对子元素进行遍历、映射和筛选。Children.toArray 方法用于将子元素转换为数组,从而可以使用数组的方法。Children.map 方法可以映射子元素并为其添加额外的 props。同时还演示了如何通过 child.type 来识别子元素的类型,从而选择性地渲染特定类型的子元素。
Suspense
功能介绍
<Suspense>是一个组件,用于在异步加载数据时展示一个 fallback 视图
接口定义
<Suspense fallback={<FallbackComponent />}>
<LazyComponent />
</Suspense>
fallback: 用于定义在组件加载期间显示的 fallback 视图;<LazyComponent />: 表示需要异步加载的组件。
示例
import Inula, { lazy, Suspense } from 'openinula';
// 异步加载组件
const AsyncComponent = lazy(() => import('./AsyncComponent'));
const App = () => (
<div>
{/* 在加载异步组件时显示fallback元素 */}
<Suspense fallback={<div>Loading...</div>}>
<AsyncComponent />
</Suspense>
</div>
);
// <AsyncComponent> 组件可以是一个普通的函数式组件或类组件
export default AsyncComponent = () => {
// 随意模拟一个异步加载的操作
setTimeout(() => {
console.log('AsyncComponent is loaded.');
}, 2000);
return <div>Async Component</div>;
};
在上面的示例中,使用 lazy 函数来异步加载一个组件 <AsyncComponent>。然后,使用 <Suspense> 组件来包裹异步组件,并在加载组件时显示 fallback 元素。在这个示例中,“fallback ...” 文本。
钩子函数
在 openInula 中,可以使用钩子函数来在函数组件中添加状态和其他特性函数,从而允许我们在函数组件中使用类组件拥有的功能。通过使用钩子函数,我们可以更方便地编写和管理React组件的状态和行为,使代码更简洁、可读性更高。
钩子函数可以让我们在函数组件中使用状态管理、生命周期方法、副作用等功能。常用的钩子函数包括 useState、useEffect、useContext、useReducer 等。
注意:
- 只能在组件的顶层或自己的 Hook 中调用钩子函数。
- 不能在循环语句或条件语句中调用钩子函数。
useState
功能介绍
useState 是一个用于在函数组件中添加状态( state )的钩子函数。使用 useState 可以让函数组件拥有类组件的状态管理能力。
接口定义
const [state, setState] = useState(initialState);
initialState:状态的初始值,在组件挂载时使用。
useState 的返回值:
state:表示当前状态的值;setState:用于更新状态的函数,可以传递新的状态值或一个函数来计算新的状态值。
示例
import Inula, { useState } from "openinula";
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(prevCount => prevCount + 1);
};
const decrement = () => {
setCount(prevCount => prevCount - 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
useCallback
功能介绍
useCallback 是一个用于优化性能的钩子函数,它可以用来缓存函数引用,避免在每次渲染时重新创建函数。这对于将函数作为 props 传递给子组件时特别有用,可以避免不必要的重新渲染。
接口定义
const cachedFn = useCallback(callback: T, deps: DependencyList)
callback:要缓存的回调函数;deps:一个数组,包含了所有可以作为依赖项的值,当依赖项发生变化时,callback函数会被重新创建。如果依赖项列表为空,那么每次渲染都会返回相同的缓存函数。
useCallback的返回值:
cachedFn:若是初次渲染,返回已传入的callback函数,当依赖项deps无变化时,返回上次渲染缓存的callback函数,当依赖项deps有变化时,则返回这次渲染传入的callback函数。
示例
import Inula, { useState, useCallback } from 'openinula';
function App() {
const [count, setCount] = useState(0);
// 使用 useCallback 缓存 handleIncrement 函数
const handleIncrement = useCallback(() => {
setCount(count + 1);
}, [count]); // 仅当 count 发生变化时,才会重新创建函数
return (
<div>
<p>Count: {count}</p>
<button onClick={handleIncrement}>Increment</button>
</div>
);
}
注意:使用
useCallback,可以确保每次渲染时都使用相同的函数引用,从而减少不必要的函数重新创建,提高性能。这在传递回调函数给子组件或在使用useEffect时尤为有用,因为它可以防止因为函数的重新创建而导致子组件的不必要重新渲染。
useContext
功能介绍
useContext 用于在组件之间共享状态。通过使用 useContext,可以避免在组件树中手动传递 props,从而更方便地访问共享的数据。
接口定义
const value = useContext(context: Context<T>)
context:是 openInula 提供的上下文,可以通过createContext创建,useContext接收上下文并返回该上下文当前值。
useContext的返回值:
value:其返回值是一个包含Provider的值,其中value.Provider是由最近的上层上下文提供,如果没有找到匹配的上层上下文,则会使用上下文对象的默认值。
示例
import Inula, { createContext, useContext, useState } from 'openinula';
// 创建一个上下文对象
const Context = createContext();
// 组件A:向上下文中提供一个共享的数据
const ComponentA = () => {
const [count, setCount] = useState(0);
return (
<Context.Provider value={count}>
<button onClick={() => setCount(count + 1)}>增加</button>
<ComponentB />
</Context.Provider>
);
}
// 组件B:从上下文中获取共享的数据
const ComponentB = () => {
const count = useContext(Context);
return <div>当前计数:{count}</div>;
}
// 主组件
const App = () => {
return <ComponentA />;
}
在这个示例中,<ComponentA> 创建了一个共享的数据 count,它使用 useState 来进行状态管理,并将数据存储在上下文对象 Context.Provider 中。<ComponentB> 使用 useContext 从 Context 中获取共享的数据 count。最后,<App> 组件作为根组件渲染 <ComponentA>,这样就能够在整个组件树中共享 count 数据。
说明:在函数组件中,
useContext代替了类组件中的Consumer。
useEffect
功能介绍
useEffect 是一个用于处理副作用(如数据获取、订阅、DOM 操作等)的钩子函数。它在组件的生命周期中执行,并且可以用于在组件渲染后执行一些操作,或在组件卸载前执行一些清理工作。
接口定义
useEffect(create: EffectCallBack, deps?: DependencyList): void
create:一个函数,用于定义副作用操作。该函数在每次渲染后都会被调用;deps:一个数组,指定了哪些依赖项的变化会触发create的重新执行。如果不传递这个数组,那么create将在每次渲染后都被调用。如果依赖项列表为空,那么create将只在组件挂载和卸载时执行。
示例
- 示例一
import Inula, { useState, useEffect } from "openinula";
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `点击次数:${count}`;
}, [count]);
return (
<div>
<p>点击次数: {count}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
</div>
);
}
在上述示例中,useEffect 可以用于数据的获取,当点击 <button> 时,就会更新标题的点击次数。
- 示例二:
import Inula, { useState, useEffect } from "openinula";
function Timer() {
const [time, setTime] = useState(0);
useEffect(() => {
const intervalId = setInterval(() => {
setTime(time + 1);
}, 1000);
return () => {
clearInterval(intervalId);
};
}, [time]);
return <div>Time: {time}</div>;
}
function App() {
const [showTimer, setShowTimer] = useState(true);
return (
<div>
{showTimer && <Timer />}
<button onClick={() => setShowTimer(!showTimer)}>
{showTimer ? 'Hide Timer' : 'Show Timer'}
</button>
</div>
);
}
在示例二中,useEffect 可以用于设置和清理定时器。
- 示例三:
useEffect(() => {
// 更新元素样式
document.getElementById('myElement').style.color = 'red';
// 添加元素
const newElement = document.createElement('div');
document.body.appendChild(newElement);
// 移除元素
return () => {
document.body.removeChild(newElement);
};
}, []);
上述示例展示 useEffect 的DOM操作,可以在 useEffect 中进行DOM操作,比如更新元素的样式、添加/移除元素等。
- 示例四:
根据依赖项的变化,useEffect 的行为会有所不同。在函数组件中,通常使用 useEffect 来模拟组件的生命周期。
useEffect(() => {
// componentDidMount
console.log('Component mounted');
// componentDidUpdate
return () => {
// componentWillUnmount
console.log('Component unmounted');
};
}, []);
useEffect(() => {
// componentDidUpdate
console.log('Component updated');
}, [data]);
上述示例中,使用 useEffect 来模拟组件的生命周期方法,比如 componentDidMount、componentDidUpdate 和 componentWillUnmount。
useLayoutEffect
功能介绍
useLayoutEffect 是一个钩子函数,用于在 DOM 更新完成后同步执行副作用操作。与 useEffect 不同,useLayoutEffect 会在 DOM 更新之后、浏览器绘制之前同步执行,因此适用于需要立即操作 DOM 的情况。
接口定义
useLayoutEffect(create: EffectCallBack, deps?: DependencyList): void;
create:一个函数,用于定义副作用操作。该函数会在 DOM 更新完成后、浏览器绘制之前立即执行;deps:一个数组,指定了哪些依赖项的变化会触发create的重新执行。如果不传递这个数组,那么create将在每次渲染后都被调用。如果依赖项列表为空,那么create将只在组件挂载和卸载时执行。
示例
import Inula, { useState, useLayoutEffect } from 'openinula';
function App() {
const [width, setWidth] = useState(0);
useLayoutEffect(() => {
const handleResize = () => {
setWidth(window.innerWidth);
};
window.addEventListener('resize', handleResize);
handleResize(); // 初始渲染时执行一次
return () => {
window.removeEventListener('resize', handleResize);
};
}, []); // 仅在挂载和卸载时执行
return (
<div>
<p>Window Width: {width}</p>
</div>
);
}
在上述示例中,useLayoutEffect 用于更新页面上窗口的宽度。它添加了一个 resize 事件监听器,在窗口大小变化时同步更新 width 状态。注意,这里使用了空数组作为依赖项,这意味着 useLayoutEffect 只在组件挂载和卸载时执行。
注意:
useEffect和useLayoutEffect都用于在组件渲染时执行副作用操作,但是主要区别与执行的时机。useEffect会在组件渲染完成后异步执行,不会阻塞组件的渲染过程。而useLayoutEffect会在组件渲染完成后同步执行,会阻塞组件的渲染过程。因此,如果需要在组件渲染完成后立即执行某些操作,可以使用useLayoutEffect,否则可以使用useEffect。
import Inula, { useState, useEffect, useLayoutEffect } from 'openinula';
const App = () => {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('useEffect');
document.title = `Count: ${count}`;
}, [count]);
useLayoutEffect(() => {
console.log('useLayoutEffect');
document.title = `Count: ${count}`;
}, [count]);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
在上面的例子中,若使用 useEffect 来更新页面标题,它会在组件渲染完成后执行,并且只有在 count 发生变化时才会执行。若使用 useLayoutEffect 来更新页面标题,它会在组件渲染完成后立即执行,并且只有在 count 发生变化时才会执行。
注意: 由于
useLayoutEffect是同步执行的,如果操作的是 DOM,可能会影响页面性能。在大多数情况下,使用useEffect就足够了,因为它会在 DOM 更新完成后异步执行副作用,不会阻塞浏览器的渲染。只有在需要即时操作 DOM 以获取测量值等情况下,才考虑使用useLayoutEffect。
useMemo
功能介绍
useMemo 是一个用于性能优化的钩子函数,它用于在组件重新渲染时,缓存和返回一个计算结果,以避免不必要的重复计算。
接口定义
const cachedValue = useMemo(create, deps)
create:一个函数,用于计算需要缓存的值;deps:一个数组,指定了哪些依赖项的变化会触发create的重新执行。当依赖项发生变化时,create将会被重新执行,返回一个新的缓存值。如果依赖项列表为空,那么create只在组件挂载时执行。
useMemo 的返回值:
cachedValue:要缓存计算值的函数,可以为任意类型,如果deps没有发生变化,会直接返回相同的值,如果deps发生变化,则调用create,并返回新的结果,然后缓存这个结果。
示例
import Inula, { useState, useMemo } from 'openinula';
function ExpensiveComponent({ number }) {
// 模拟一个昂贵的计算
const expensiveCalculation = () => {
console.log("Calculating...");
let result = 0;
for (let i = 0; i < number * 1000000; i++) {
result += i;
}
return result;
};
// 使用 useMemo 缓存计算结果
const memoizedValue = useMemo(() => expensiveCalculation(), [number]);
return (
<div>
<p>Number: {number}</p>
<p>Calculated Value: {memoizedValue}</p>
</div>
);
}
function App() {
const [count, setCount] = useState(1);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment</button>
<ExpensiveComponent number={count} />
</div>
);
}
在这个示例中,ExpensiveComponent 组件包含一个昂贵的计算操作,该计算在每次渲染时都会执行。通过使用 useMemo,可以缓存计算结果,只有在 number 发生变化时才重新计算。这可以有效避免在每次渲染时都执行昂贵的计算,从而提高性能。
注意:
useMemo可以在性能优化方面发挥作用,但不应该被滥用。在某些情况下,过度使用useMemo可能会导致代码更难理解。只有在确实需要避免重复计算的情况下,才应该使用useMemo。
useReducer
功能介绍
useReducer 是一个用于管理状态的钩子函数,它结合了 useState 和自定义的状态更新逻辑,通常用于管理复杂的状态逻辑。
接口定义
const [state, dispatch] = useReducer(reducer, initialState, init?);
reducer:一个函数,用于定义状态更新逻辑。它接收当前状态 state 和一个表示动作的对象 action,并返回新的状态;initialState:初始状态的值, 计算逻辑取决于init参数;init:可选参数,用于计算初始状态值的函数,如果存在,则使用init(initialState)的执行结果作为初始值,否则使用initialState。
useReducer 有两个返回值:
state:当前状态的值;dispatch:一个函数,用于触发状态更新。它接收一个表示动作的对象,并将该对象传递给reducer,从而更新状态state。
示例
import Inula, { useReducer } from 'openinula';
// 定义 reducer 函数
const counterReducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
};
function App() {
// 使用 useReducer 来管理状态
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
</div>
);
}
在上述示例中,使用了 useReducer 以允许通过 dispatch 函数触发 state 的更新。在需要更新状态时,可以向 dispatch 传递一个表示动作的对象(action),随后 reducer 函数,即 counterReducer 函数将根据动作类型 (action.type)为INCREMENT 还是 DECREMENT 更新状态 state。
注意:相比使用多个
useState来管理状态,useReducer适用于具有复杂状态逻辑的情况,它能够更好地组织和管理状态变化。
useRef
功能介绍
useRef 用于创建一个引用对象,可以用来存储组件的引用或持久化的值。与 useState 不同,useRef 创建的引用对象在更新时不会触发组件重新渲染。
接口定义
const refContainer = useRef(initialValue);
initialValue:引用对象的初始值,可以是任何值,通常设置为null。
useRef 的返回值:
refContainer:一个引用对象,其中的current属性可以被用于存储和访问值。
示例
import Inula, { useRef } from 'openinula';
function RefDemo() {
const inputRef = useRef(null);
const handleButtonClick = () => {
// 使用 ref 获取 input 元素的引用,并设置焦点
inputRef.current.focus();
};
return (
<div>
<input type="text" ref={inputRef} />
<button onClick={handleButtonClick}>设置焦点</button>
</div>
);
}
上面的示例中,创建了一个函数组件 RefDemo,使用 useRef 钩子创建了一个名为 inputRef 的 ref 对象,并在 JSX 中将这个 ref 对象绑定到输入框元素上。
当按钮被点击时,handleButtonClick 函数会使用 inputRef.current 来获取输入框的 DOM 元素,并调用 focus() 方法来设置焦点。这样,点击按钮后,输入框就会自动获得焦点。
注意:
useRef不仅适用于获取 DOM 元素的引用,还可以在函数组件中存储和访问任意可变值。与state一样,ref可以指向任何东西:比如字符串、对象甚至函数。与state不同的是,ref是一个带有当前属性的普通对象,可以读取和修改。
存储变量
import Inula, { useState, useRef, useCallback } from 'openinula'
function ClickWatch() {
let ref = useRef(0);
const handleClick = useCallback(() => {
ref.current = ref.current + 1;
alert('You clicked ' + ref.current + ' times!');
}, [])
return (
<button onClick={handleClick}>
Click me!
</button>
);
}
在上述示例中,将在每次点击后增加 ref.current。
注意:组件不会在每次递增时都触发重新渲染。 像
state一样,ref在重新渲染时被 openInula 保留。然而,setState将会重新渲染组件,修改ref则不会。
useImperativeHandle
功能介绍
useImperativeHandle 是一个用于定制在父组件中访问子组件实例方法的钩子函数。通常情况下,openInula 鼓励使用 props 和 state 来进行组件之间的通信,但在某些情况下,可能需要从父组件中直接调用子组件。
接口定义
useImperativeHandle(ref, createHandle, deps?):void;
ref:一个ref引用对象,通常通过createRef()创建;createHandle:一个函数,用于创建子组件实例中需要暴露给父组件的方法或属性;deps:可选参数,一个依赖数组,当依赖项变化时,会重新计算createHandle。通常在这里指定子组件的内部状态或其他信息,以确保暴露给父组件的方法在这些依赖项变化时得到更新。
示例
import Inula, { useRef, useImperativeHandle, forwardRef } from 'openinula';
// 子组件
const ChildComponent = forwardRef((props, ref) => {
const inputRef = useRef();
// 暴露给父组件的方法
useImperativeHandle(ref, () => ({
focusInput: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} />;
});
// 父组件
function App() {
const childRef = useRef();
const handleFocusInput = () => {
childRef.current.focusInput();
};
return (
<div>
<ChildComponent ref={childRef} />
<button onClick={handleFocusInput}>Focus Input</button>
</div>
);
}
在这个例子中,<ChildComponent> 使用 useImperativeHandle 来创建一个名为 focusInput 的方法,父组件可以通过 ref 调用这个方法来聚焦输入框。通过这种方式,你可以在需要时从父组件中控制子组件的行为。
注意:
useImperativeHandle应该避免过度使用,因为它破坏了组件之间的封装性,通常情况下应该优先考虑使用props和状态来实现组件之间的通信。
APIs
render
功能介绍
在指定的 DOM 元素下渲染 openInula 组件。
接口定义
function render(children: any, container: Container, callback?: any): Element | Text | null
children:需要渲染的 openInula 组件;container:DOM元素,children将会渲染在该元素中;callback:挂载后的回调函数。
示例
import { render } from 'openinula';
function App() {
return <h1>hello</h1>;
}
render(<App />, document.getElementById('div'));
上述示例展示在 DOM 元素 <div> 下挂载了组件 <App>,组件 <App> 返回了一个 html 标题元素。
createElement
功能介绍
当无法使用 JSX 语法时,可以使用 createElement 创建一个 openInula 元素。
接口定义
function createElement(type: any, setting: any, ...children: any[]): {
[x: string]: any;
vtype: number;
src: null;
type: any;
key: any;
ref: any;
props: any;
}
type:创建的组件类型;setting:创建元素时传入的props对象。...children:组件的零个或多个子节点。
示例
import { createElement } from 'openinula';
function Welcome() {
return createElement(
'h1',
{ className: 'welcome' },
'你好'
);
}
在上述示例中,创建了一个 <Welcome> 函数组件,这个组件返回了一个 h1 元素,其中包含了一个 className 属性,属性值为 'welcome',并包含文本内容'你好'。
createPortal
功能介绍
createPortal 可以将某些元素的子元素渲染到 DOM 中的不同位置。
接口定义
function createPortal(children: any, realNode: any, key?: string): PortalType
type PortalType = {
vtype: number;
key: null | string;
realNode: any;
children: any;
};
children:需要渲染的 openInula 组件;readNode:是某个已经存在的DOM节点,children将会渲染在该元素中;key:可选参数,用作 portal key 的独特字符串或数字。
示例
import { createPortal } from 'openinula';
function Component() {
return (
<div>
<p>这个子节点被放置在父节点 div 中</p>
{createPortal(
<p>这个子节点被放置在 document body 中</p>,
document.body
)}
</div>
);
}
在这段代码中,createPortal 函数传入的第一个参数是一个 <p> 标签,第二个参数是 document.body,意味着这个<p> 标签将被创建并插入到文档的 body 元素中。这样,即使父组件的 <div> 被其他元素覆盖,这个 <p> 标签也会被放置在 body 的最顶层,而不会被覆盖。
注意:portal 只改变 DOM 元素所处的位置,而不改变组件在 React 树中的位置。子节点仍能访问父节点的上下文,且事件仍将从子节点冒泡到父节点。
createRef
功能介绍
createRef 用于在类组件中声明一个 ref 对象,该对象可以用来引用DOM元素或组件实例。
接口定义
function createRef(): RefType
type RefType = {
current: any;
};
示例
import { Component, createRef } from 'openinula';
class App extends Component {
inputRef = createRef();
handleClick = () => {
this.inputRef.current.value++;
}
render() {
return (
<>
<input ref={this.inputRef} />
<button onClick={this.handleClick}>
点击增加
</button>
</>
);
}
}
上述示例创建了一个 App 组件,通过调用 createRef() 函数创建一个 ref 对象 inputRef。在渲染时,handleClick 方法会被调用,它会通过 this.inputRef.current.value++ 来增加 input 元素的值。input 元素通过 ref 属性引用 inputRef。这样就可以通过 this.inputRef 来操作输入框。
forwardRef
功能介绍
forwardRef 允许组件通过参数中的 ref 将一个 DOM 节点暴露给父组件。
类型定义
function forwardRef(render: Function): {
vtype: number;
$$typeof: number;
render: Function;
}
render: 是一个组件渲染函数,可以接收props和ref作为参数。
示例
import { forwardRef, useRef } from 'openinula';
const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} ref={ref} />
</label>
);
});
export default function Form() {
const ref = useRef(null);
function handleClick() {
ref.current.value++;
}
return (
<form>
<MyInput label="Auto add:" ref={ref} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}
上述示例中,MyInput 组件通过 forwardRef 将 input 输入框暴露给父组件 Form,这样 Form 可以通过该 ref 操作该 inupt。
lazy
功能介绍
lazy 声明一个懒加载组件,直到该组件第一次被加载前,才会加载该组件的代码。
接口定义
function lazy<T>(promiseCtor: () => PromiseType<{
default: T;
}>): LazyComponent<T, LazyContent<T>>
promiseCtor是一个返回Promise对象的函数,第一次渲染组件前,openInula 才会调用该函数获取组件。
示例
import { lazy } from 'openinula';
const LazyComp = lazy(() => import { App } from './App');
在上述示例中,使用lazy函数来异步加载<App>组件。lazy 函数接受一个函数,该函数返回一个import()语句,用于动态加载组件模块。
memo
功能介绍
使用 memo 函数包裹组件,通常情况下,只要该组件的 props 没有改变,可以防止组件的重新渲染。
接口定义
function memo<Props>(type: any, compare?: ((oldProps: Props, newProps: Props) => boolean) | undefined): {
vtype: number;
$$typeof: number;
type: any;
compare: ((oldProps: Props, newProps: Props) => boolean) | null;
}
type:需要进行记忆化的组件;compare:可选参数,是一个函数,接收一对新旧props,判断两者是否完全相同。
示例
import { memo, useState } from 'openinula';
const MyComponent = ({ name }) => {
return <div>Hello, {name}!</div>;
};
// 使用memo将MyComponent包装
const MemoizedComponent = memo(MyComponent);
const App = () => {
const [count, setCount] = React.useState(0);
const handleClick = () => {
setCount(prevCount => prevCount + 1);
};
return (
<div>
{/* 每次更新会重新渲染MyComponent */}
<MyComponent name="Alice" />
{/* 使用MemoizedComponent,只有在name发生变化时才会重新渲染 */}
<MemoizedComponent name="Bob" />
<div>
Current count: {count}
<button onClick={handleClick}>Increment</button>
</div>
</div>
);
};
在上述示例中,创建了两个组件,一个是普通 MyComponent 组件,一个是使用 memo 包装的 MemoizedComponent。使用 memo 包装的 MemoizedComponent 只会在 props 的 name 属性发生变化时重新渲染。在点击按钮时,由于 MemoizedComponent 的 name 属性没有变化,所以不会重新渲染。
注意:在实际场景中,memo可以有效地避免无需更新的组件重新渲染,提高应用的性能。
createContext
功能介绍
createContext 创建一个可供不同组件调用的上下文对象,这样各层组件之间不需要通过逐层传递信息。
接口定义
function createContext<T>(val: T): ContextType<T>
val: 是上下文的初始值。
示例
import { createContext } from 'openinula';
const LanguageContext = createContext('zh-cn');
function Header() {
const locale = React.useContext(LanguageContext);
return <p>Header {locale}</p>;
}
function Footer() {
const locale = React.useContext(LanguageContext);
return <p>Footer {locale}</p>;
}
function Page() {
const locale = React.useContext(LanguageContext);
return (
<div>
<Header />
<p>Page {locale}</p>
<Footer />
</div>
);
}
上述示例展示了 createContext 方法的使用方式,可以通过 createContext 创建一个供全局组件使用的 LanguageContext ,组件可以直接获取,不需要通过逐级传值的方式进行获取。
cloneElement
功能介绍
cloneElement 可以从一个已有元素创建一个新的 Inula 元素。
接口定义
function cloneElement(element: any, setting: any, ...children: any[]): {
[x: string]: any;
vtype: number;
src: null;
type: any;
key: any;
ref: any;
props: any;
}
element:需要克隆的 Inula 元素;setting:克隆后元素的props;...children:是零个或多个子节点,如果不传入,将使用原始element.props.children。
示例
import { cloneElement } from 'openinula';
function Time({ time }) {
return createElement(
'h1',
{ className: 'clock' },
'Time is ',
createElement('i', null, time),
);
}
function Clock() {
const time = new Date().toLocaleTimeString();
return cloneElement(
<Time />,
{ time: time }
);
}
findDOMNode
功能介绍
findDOMNode 获取类式组件实例对应的浏览器 DOM 节点。
接口定义
function findDOMNode(domOrEle?: Element | undefined): null | Element | Text
domOrEle: 需要获取对应节点的Component子类的实例。
示例
import { Component, findDOMNode } from 'openinula';
class App extends Component {
componentDidMount() {
const input = findDOMNode(this);
input.select()
}
render() {
return <input defaultValue="你好" />
}
}
在上述代码中,通过 findDOMNode 获取组件中的DOM节点。在 componentDidMount 生命周期方法中,通过调用 findDOMNode(this) 来获取组件实例对应的DOM节点,并将其赋值给 input 变量。然后,通过调用 input.select() 来选中输入框中的文本。
flushSync
功能介绍
flushSync 是一种同步更新界面的方法,它可以确保在当前渲染中的所有状态更新都被同步处理,并且能在更新完毕后立即执行其他代码。
接口定义
function flushSync(fn: any): any
fn:表示一个函数作为参数,在这个回调函数中执行需要同步处理的更新操作。
示例
import Inula, { useState, flushSync } from 'openinula';
function App() {
const [count, setCount] = useState(0);
const handleClick = () => {
flushSync(() => {
setCount(count + 1);
console.log(count); // 输出0,因为更新还未同步处理完成
});
console.log(count); // 输出1,因为更新已经同步处理完成
};
return (
<div>
<button onClick={handleClick}>Increment</button>
<p>Count: {count}</p>
</div>
);
}
在上面的代码中,使用 useState 来创建一个状态 count,然后在按钮的点击事件处理函数 handleClick 中使用 flushSync 来确保 setCount 的更新操作被同步处理。注意,在调用 flushSync 之后,可以立即访问到最新的 count 值。
unmountComponentAtNode
功能介绍
从 DOM 中移除一个已挂载的 Inula 组件。
接口定义
function unmountComponentAtNode(container: Container): boolean
container是一个DOM节点。
示例
import { render, unmountComponentAtNode } from 'openinula';
function App() {
return <h1>hello</h1>;
}
const rootNode = document.getElementById('root');
render(<App />, rootNode);
// ...
unmountComponentAtNode(rootNode);
在上述代码中,定义了一个 App 组件,并通过 render 函数将其渲染到 rootNode 节点下,最后通过unmountComponentAtNode 函数将 rootNode 节点下的组件卸载,即从 DOM 中移除组件。
Tools
isFragment
功能介绍
isFragment 用于检查给定的 Inula 组件是否是一个 Fragment 。
接口定义
function isFragment(ele: any): ele is InulaElement;
interface InulaElement<
P = any,
T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>
> {
type: T;
props: P;
key: Key | null;
}
type JSXElementConstructor<P> =
| ((props: P) => InulaElement<any, any> | null)
| (new (props: P) => Component<any, any>);
ele:输入任意待检查组件。
示例
import Inula, { isFragment } from 'openinula';
function FragmentComponent(props) {
if (isFragment(props.children)) {
return <>{props.children}</>;
} else {
return <div>{props.children}</div>;
}
}
function App() {
return (
<FragmentComponent>
<p>Some text</p>
</FragmentComponent>
);
}
isElement
功能介绍
isElement 用于检查给定的值是否是一个 Inula 组件。
接口定义
function isElement(ele: any): ele is InulaElement;
ele:输入的任意待检查元素。
示例
import Inula, { isElement } from 'openinula';
const componentElement = <InulaComponent />;
const htmlElement = <div>Hello, World!</div>;
console.log(isElement(componentElement)); // 输出: true
console.log(isElement(htmlElement)); // 输出: true
console.log(isElement('string')); // 输出: false
isValidElement
功能介绍
isValidElement 用于检测结构体是否为合法的 Element 组件
接口定义
function isValidElement<P>(element: KVObject | null | undefined): element is HorizonElement<P>;
type KVObject = Record<string, any>;
ele:输入的任意待检查元素。
示例
import Inula, { isValidElement } from 'openinula';
const element = <div>Hello, World!</div>;
console.log(isValidElement(element)); // 输出: true
console.log(isValidElement('Hello World!')); // 输出: false
isValidElementType
功能介绍
isValidElementType 用于检查给定的值是否是有效的 Inula 组件类型
接口定义
function isValidElementType(type: any): boolean;
type:输入的任意待检查元素类型。
示例
import Inula, { isValidElementType } from 'openinula';
console.log(isValidElementType('div')); // 输出: false
console.log(isValidElementType(InulaComponent)); // 输出: true
console.log(isValidElementType(() => {})); // 输出: false
console.log(isValidElementType('string')); // 输出: true
isForwardRef
功能介绍
isForwardRef 用于检查给定的 Inula 元素是否是一个 ForwardRef(转发引用)对象。
接口定义
function isForwardRef(ele: any): ele is InulaElement;
ele:输入的任意待检查元素。
示例
import Inula, { forwardRef, isForwardRef } from 'openinula';
const ForwardedComponent = forwardRef((props, ref) => (
<div ref={ref}>Forwarded Component</div>
));
console.log(isForwardRef(ForwardedComponent)); // 输出: true
console.log(isForwardRef(() => {})); // 输出: false
isLazy
功能介绍
isLazy 用于检查给定的值是否是一个懒加载组件。
接口定义
function isLazy(ele: any): ele is LazyComponent<any>;
type LazyComponent<T extends ComponentType<any>> = ExoticComponent<ComponentPropsWithRef<T>> & {
readonly _result: T;
};
interface ExoticComponent<P = KVObject> {
(props: P): InulaElement | null;
}
ele:输入的任意待检查元素。
示例
import Inula, { lazy, isLazy } from 'openinula';
const LazyComponent = lazy(() => import('./LazyComponent'));
console.log(isLazy(LazyComponent)); // 输出: true
console.log(isLazy(() => {})); // 输出: false
isMemo
功能介绍
isMemo 用于检查给定的值是否是一个 memoized 组件
接口定义
function isMemo(ele: any): ele is MemoComponent<any>;
type MemoComponent<T extends ComponentType<any>> = ExoticComponent<ComponentPropsWithRef<T>> & {
readonly type: T;
displayName?: string;
};
ele:输入的任意待检查元素。
示例
import Inula, { memo, isMemo } from 'openinula';
const MemoizedComponent = memo(() => (
<div>Memoized Component</div>
));
console.log(isMemo(MemoizedComponent)); // 输出: true
console.log(isMemo(() => {})); // 输出: false
isPortal
功能介绍
isPortal 用于检查给定的值是否是一个 Portal 组件
接口定义
function isPortal(ele: any): ele is InulaElement;
ele:输入的任意待检查元素
示例
import Inula, { isPortal, Component, createPortal } from 'openinula';
class PortalComponent extends Component {
render() {
return createPortal(
this.props.children,
document.getElementById('root')
);
}
}
console.log(isPortal(<PortalComponent />)); // 输出: true
console.log(isPortal(() => {})); // 输出: false
isContextProvider
功能介绍
isContextProvider 用于检查给定的值是否是一个 Provider 组件
接口定义
function isContextProvider(ele: any): ele is InulaElement;
ele:输入的任意待检查元素
示例
import Inula, { createContext, isContextProvider } from 'openinula';
const MyContext = createContext();
console.log(isContextProvider(MyContext.Provider)); // 输出: true
console.log(isContextProvider(() => {})); // 输出: false
isContextConsumer
功能介绍
isContextConsumer 用于检查给定的值是否是一个 Context 组件
接口定义
function isContextConsumer(ele: any): ele is InulaElement;
ele:输入的任意待检查元素
示例
import Inula, { createContext, isContextProvider } from 'openinula';
const MyContext = createContext();
console.log(isContextProvider(MyContext.Consumer)); // 输出: true
console.log(isContextProvider(() => {})); // 输出: false
