1、Props
组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props。
A.函数组件通过props获取组件传递的参数
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
</FancyBorder>
);
}
function WelcomeDialog() {
return (
<Dialog
title="Welcome"
message="Thank you for visiting our spacecraft!" />
);
}
a.声明一个响应参数的函数组件
function Person(props) {
return(
<ul>
<li>姓名:{props.name}</li>
<li>年龄:{props.age}</li>
<li>性别:{props.sex}</li>
</ul>
)
}
b.在使用的函数组件时传递参数
// 初始化数据
const p1 = {
name: "Fcc",
age: 18,
sex: "男"
}
// 渲染到组件
<Person name={p1.name} age={p1.age} sex={p1.sex}/>
B.Class组件通过props调用父类的构造函数
class Clock extends React.Component {
constructor(props) {
super(props);
}
}
C.通过 {``props.``children}
获取父组件的嵌套内容
a.在父组件嵌套其他组件内容
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
b.通过 {``props.``children}
获取父组件的嵌套内容
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
D.通过props传递其他组件
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
);
}
function App() {
return (
<SplitPane
left={
<Contacts />
}
right={
<Chat />
} />
);
}
E.通过props传递及获取参数和组件
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
{props.children}
</FancyBorder>
);
}
class SignUpDialog extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
this.state = {login: ''};
}
render() {
return (
<Dialog title="Mars Exploration Program"
message="How should we refer to you?">
<input value={this.state.login}
onChange={this.handleChange} />
<button onClick={this.handleSignUp}>
Sign Me Up!
</button>
</Dialog>
);
}
handleChange(e) {
this.setState({login: e.target.value});
}
handleSignUp() {
alert(`Welcome aboard, ${this.state.login}!`);
}
}
2、State
A.State的定义
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
list:[
name: "Fcc",
age: 18,
sex: "男"
],
value: ""
};
}
}
B.读取State的值
通过this.state直接读取
this.state.list.value;
C.通过this.setState()
来时刻更新组件 state
不能直接通过this.state来修改属性的值,不会重新渲染组件
应该使用构造函数修改State的值
构造函数是唯一可以给 this.state
赋值的途径
this.setState({comment: 'Hello'});
D.State的更新可能是异步的
出于性能考虑,React 可能会把多个 setState()
调用合并成一个调用。
3、Refs
A.定义一个ref属性
React推荐通过箭头函数来进行绑定
<input ref={inputValue => this.inputValue = inputValue}/>
- 通过调用
React.createRef
创建了一个 React ref 并将其赋值给 inputValue 变量。 - 使用ref属性进行变量绑定
const inputValue = React.createRef();
<input ref={inputValue}/>
B.通过refs属性取值
const inputValue = this.refs.inputValue.value.trim();
C.Refs在文件 input 标签中使用
D.ref 转发
Ref 转发是一个可选特性,其允许某些组件接收 ref
,并将其向下传递(换句话说,“转发”它)给子组件。
FancyButton
使用 React.forwardRef
来获取传递给它的 ref
,然后转发到它渲染的 DOM button
:
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// 可以直接获取 DOM button 的 ref:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
使用 FancyButton
的组件可以获取底层 DOM 节点 button
的 ref ,并在必要时访问
- 通过调用
React.createRef
创建了一个 React ref 并将其赋值给ref
变量。 - 通过指定
ref
为 JSX 属性,将其向下传递给<FancyButton ref={ref}>
。 - React 传递
ref
给forwardRef
内函数(props, ref) => ...
,作为其第二个参数。 - 向下转发该
ref
参数到<button ref={ref}>
,将其指定为 JSX 属性。 - 当 ref 挂载完成,
ref.current
将指向<button>
DOM 节点。注意 第二个参数
ref
只在使用React.forwardRef
定义组件时存在。常规函数和 class 组件不接收ref
参数,且 props 中也不存在ref
。 Ref 转发不仅限于 DOM 组件,可以转发 refs 到 class 组件实例中。
E. **forwardRef**
官方不推荐使用
在组件库中使用 **forwardRef**
时,应当将其视为一个破坏性更改。因为库可能会有明显不同的行为(例如 refs 被分配给了谁,以及导出了什么类型),并且这样可能会导致依赖旧行为的应用和其他库崩溃。
出于同样的原因,当 **React.forwardRef**
存在时有条件地使用它也是不推荐的:它改变了库的行为,并在升级 React 自身时破环用户的应用。
F.在高阶组件中转发 refs
1,一个输出组件 props 到控制台的 HOC 示例
function logProps(WrappedComponent) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
return <WrappedComponent {...this.props} />;
}
}
return LogProps;
}
“logProps” HOC 透传(pass through)所有 props
到其包裹的组件,所以渲染结果将是相同的。例如:可以使用该 HOC 记录所有传递到 “fancy button” 组件的 props
class FancyButton extends React.Component {
focus() {
// ...
}
// ...
}
// 导出 LogProps,而不是 FancyButton。
// 虽然它也会渲染一个 FancyButton。
export default logProps(FancyButton);
上面的示例有一点需要注意:refs 将不会透传下去。这是因为 ref
不是 prop 属性。就像 key
一样,其被 React 进行了特殊处理。
2,该 ref 将引用最外层的容器组件
如果对 HOC 添加 ref,该 ref 将引用最外层的容器组件,而不是被包裹的组件。这意味着用于 FancyButton
组件的 refs 实际上将被挂载到 LogProps
组件:
import FancyButton from './FancyButton';
const ref = React.createRef();
// 导入的 FancyButton 组件是高阶组件(HOC)LogProps。
// 尽管渲染结果将是一样的,
// 但 ref 将指向 LogProps 而不是内部的 FancyButton 组件!
// 这意味着不能调用例如 ref.current.focus() 这样的方法
<FancyButton
label="Click Me"
handleClick={handleClick}
ref={ref}
/>;
3,使用 React.forwardRef
API 明确地将 refs 转发到内部的组件
可以使用 React.forwardRef
API 明确地将 refs 转发到内部的 FancyButton
组件。React.forwardRef
接受一个渲染函数,其接收 props
和 ref
参数并返回一个 React 节点。
function logProps(Component) {
class LogProps extends React.Component {
componentDidUpdate(prevProps) {
console.log('old props:', prevProps);
console.log('new props:', this.props);
}
render() {
const {forwardedRef, ...rest} = this.props;
// 将自定义的 prop 属性 “forwardedRef” 定义为 ref
return <Component ref={forwardedRef} {...rest} />;
}
}
// 注意 React.forwardRef 回调的第二个参数 “ref”。
// 可以将其作为常规 prop 属性传递给 LogProps,例如 “forwardedRef”
// 然后它就可以被挂载到被 LogProps 包裹的子组件上。
return React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
}
G.在 DevTools 中显示自定义名称
React.forwardRef
接受一个渲染函数。React DevTools 使用该函数来决定为 ref 转发组件显示的内容。
1,以下组件将在 DevTools 中显示为 “ForwardRef”
const WrappedComponent = React.forwardRef((props, ref) => {
return <LogProps {...props} forwardedRef={ref} />;
});
2,命名了渲染函数,DevTools 也将包含其名称(例如 “ForwardRef(myFunction)”)
const WrappedComponent = React.forwardRef(
function myFunction(props, ref) {
return <LogProps {...props} forwardedRef={ref} />;
}
);
3,可以设置函数的 displayName
属性来包含被包裹组件的名称
function logProps(Component) {
class LogProps extends React.Component {
// ...
}
function forwardRef(props, ref) {
return <LogProps {...props} forwardedRef={ref} />;
}
// 在 DevTools 中为该组件提供一个更有用的显示名。
// 例如 “ForwardRef(logProps(MyComponent))”
const name = Component.displayName || Component.name;
forwardRef.displayName = `logProps(${name})`;
return React.forwardRef(forwardRef);
}