1、Props

组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props。

A.函数组件通过props获取组件传递的参数

  1. function Dialog(props) {
  2. return (
  3. <FancyBorder color="blue">
  4. <h1 className="Dialog-title">
  5. {props.title}
  6. </h1>
  7. <p className="Dialog-message">
  8. {props.message}
  9. </p>
  10. </FancyBorder>
  11. );
  12. }
  13. function WelcomeDialog() {
  14. return (
  15. <Dialog
  16. title="Welcome"
  17. message="Thank you for visiting our spacecraft!" />
  18. );
  19. }

a.声明一个响应参数的函数组件

  1. function Person(props) {
  2. return(
  3. <ul>
  4. <li>姓名:{props.name}</li>
  5. <li>年龄:{props.age}</li>
  6. <li>性别:{props.sex}</li>
  7. </ul>
  8. )
  9. }

b.在使用的函数组件时传递参数

  1. // 初始化数据
  2. const p1 = {
  3. name: "Fcc",
  4. age: 18,
  5. sex: "男"
  6. }
  7. // 渲染到组件
  8. <Person name={p1.name} age={p1.age} sex={p1.sex}/>

B.Class组件通过props调用父类的构造函数

  1. class Clock extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. }
  5. }

C.通过 {``props.``children} 获取父组件的嵌套内容

a.在父组件嵌套其他组件内容

  1. function WelcomeDialog() {
  2. return (
  3. <FancyBorder color="blue">
  4. <h1 className="Dialog-title">
  5. Welcome
  6. </h1>
  7. <p className="Dialog-message">
  8. Thank you for visiting our spacecraft!
  9. </p>
  10. </FancyBorder>
  11. );
  12. }

b.通过 {``props.``children} 获取父组件的嵌套内容

  1. function FancyBorder(props) {
  2. return (
  3. <div className={'FancyBorder FancyBorder-' + props.color}>
  4. {props.children}
  5. </div>
  6. );
  7. }

D.通过props传递其他组件

  1. function SplitPane(props) {
  2. return (
  3. <div className="SplitPane">
  4. <div className="SplitPane-left">
  5. {props.left}
  6. </div>
  7. <div className="SplitPane-right">
  8. {props.right}
  9. </div>
  10. </div>
  11. );
  12. }
  13. function App() {
  14. return (
  15. <SplitPane
  16. left={
  17. <Contacts />
  18. }
  19. right={
  20. <Chat />
  21. } />
  22. );
  23. }

E.通过props传递及获取参数和组件

  1. function Dialog(props) {
  2. return (
  3. <FancyBorder color="blue">
  4. <h1 className="Dialog-title">
  5. {props.title}
  6. </h1>
  7. <p className="Dialog-message">
  8. {props.message}
  9. </p>
  10. {props.children}
  11. </FancyBorder>
  12. );
  13. }
  14. class SignUpDialog extends React.Component {
  15. constructor(props) {
  16. super(props);
  17. this.handleChange = this.handleChange.bind(this);
  18. this.handleSignUp = this.handleSignUp.bind(this);
  19. this.state = {login: ''};
  20. }
  21. render() {
  22. return (
  23. <Dialog title="Mars Exploration Program"
  24. message="How should we refer to you?">
  25. <input value={this.state.login}
  26. onChange={this.handleChange} />
  27. <button onClick={this.handleSignUp}>
  28. Sign Me Up!
  29. </button>
  30. </Dialog>
  31. );
  32. }
  33. handleChange(e) {
  34. this.setState({login: e.target.value});
  35. }
  36. handleSignUp() {
  37. alert(`Welcome aboard, ${this.state.login}!`);
  38. }
  39. }

2、State

A.State的定义

  1. class Clock extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. this.state = {
  5. list:[
  6. name: "Fcc",
  7. age: 18,
  8. sex: "男"
  9. ],
  10. value: ""
  11. };
  12. }
  13. }

B.读取State的值

通过this.state直接读取

  1. this.state.list.value;

C.通过this.setState() 来时刻更新组件 state

不能直接通过this.state来修改属性的值,不会重新渲染组件
应该使用构造函数修改State的值
构造函数是唯一可以给 this.state 赋值的途径

  1. this.setState({comment: 'Hello'});

D.State的更新可能是异步的

出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。

3、Refs

A.定义一个ref属性

React推荐通过箭头函数来进行绑定

  1. <input ref={inputValue => this.inputValue = inputValue}/>
  1. 通过调用 React.createRef 创建了一个 React ref 并将其赋值给 inputValue 变量。
  2. 使用ref属性进行变量绑定
    1. const inputValue = React.createRef();
    2. <input ref={inputValue}/>

B.通过refs属性取值

  1. const inputValue = this.refs.inputValue.value.trim();

C.Refs在文件 input 标签中使用

6.React之表单控件

D.ref 转发

Ref 转发是一个可选特性,其允许某些组件接收 ref,并将其向下传递(换句话说,“转发”它)给子组件。
FancyButton 使用 React.forwardRef 来获取传递给它的 ref,然后转发到它渲染的 DOM button

  1. const FancyButton = React.forwardRef((props, ref) => (
  2. <button ref={ref} className="FancyButton">
  3. {props.children}
  4. </button>
  5. ));
  6. // 可以直接获取 DOM button 的 ref:
  7. const ref = React.createRef();
  8. <FancyButton ref={ref}>Click me!</FancyButton>;

使用 FancyButton 的组件可以获取底层 DOM 节点 button 的 ref ,并在必要时访问

  1. 通过调用 React.createRef 创建了一个 React ref 并将其赋值给 ref 变量。
  2. 通过指定 ref 为 JSX 属性,将其向下传递给 <FancyButton ref={ref}>
  3. React 传递 refforwardRef 内函数 (props, ref) => ...,作为其第二个参数。
  4. 向下转发该 ref 参数到 <button ref={ref}>,将其指定为 JSX 属性。
  5. 当 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 示例

  1. function logProps(WrappedComponent) {
  2. class LogProps extends React.Component {
  3. componentDidUpdate(prevProps) {
  4. console.log('old props:', prevProps);
  5. console.log('new props:', this.props);
  6. }
  7. render() {
  8. return <WrappedComponent {...this.props} />;
  9. }
  10. }
  11. return LogProps;
  12. }

“logProps” HOC 透传(pass through)所有 props 到其包裹的组件,所以渲染结果将是相同的。例如:可以使用该 HOC 记录所有传递到 “fancy button” 组件的 props

  1. class FancyButton extends React.Component {
  2. focus() {
  3. // ...
  4. }
  5. // ...
  6. }
  7. // 导出 LogProps,而不是 FancyButton。
  8. // 虽然它也会渲染一个 FancyButton。
  9. export default logProps(FancyButton);

上面的示例有一点需要注意:refs 将不会透传下去。这是因为 ref 不是 prop 属性。就像 key 一样,其被 React 进行了特殊处理。

2,该 ref 将引用最外层的容器组件

如果对 HOC 添加 ref,该 ref 将引用最外层的容器组件,而不是被包裹的组件。这意味着用于 FancyButton 组件的 refs 实际上将被挂载到 LogProps 组件:

  1. import FancyButton from './FancyButton';
  2. const ref = React.createRef();
  3. // 导入的 FancyButton 组件是高阶组件(HOC)LogProps。
  4. // 尽管渲染结果将是一样的,
  5. // 但 ref 将指向 LogProps 而不是内部的 FancyButton 组件!
  6. // 这意味着不能调用例如 ref.current.focus() 这样的方法
  7. <FancyButton
  8. label="Click Me"
  9. handleClick={handleClick}
  10. ref={ref}
  11. />;

3,使用 React.forwardRef API 明确地将 refs 转发到内部的组件

可以使用 React.forwardRef API 明确地将 refs 转发到内部的 FancyButton 组件。React.forwardRef 接受一个渲染函数,其接收 propsref 参数并返回一个 React 节点。

  1. function logProps(Component) {
  2. class LogProps extends React.Component {
  3. componentDidUpdate(prevProps) {
  4. console.log('old props:', prevProps);
  5. console.log('new props:', this.props);
  6. }
  7. render() {
  8. const {forwardedRef, ...rest} = this.props;
  9. // 将自定义的 prop 属性 “forwardedRef” 定义为 ref
  10. return <Component ref={forwardedRef} {...rest} />;
  11. }
  12. }
  13. // 注意 React.forwardRef 回调的第二个参数 “ref”。
  14. // 可以将其作为常规 prop 属性传递给 LogProps,例如 “forwardedRef”
  15. // 然后它就可以被挂载到被 LogProps 包裹的子组件上。
  16. return React.forwardRef((props, ref) => {
  17. return <LogProps {...props} forwardedRef={ref} />;
  18. });
  19. }

G.在 DevTools 中显示自定义名称

React.forwardRef 接受一个渲染函数。React DevTools 使用该函数来决定为 ref 转发组件显示的内容。

1,以下组件将在 DevTools 中显示为 “ForwardRef

  1. const WrappedComponent = React.forwardRef((props, ref) => {
  2. return <LogProps {...props} forwardedRef={ref} />;
  3. });

2,命名了渲染函数,DevTools 也将包含其名称(例如 “ForwardRef(myFunction)”)

  1. const WrappedComponent = React.forwardRef(
  2. function myFunction(props, ref) {
  3. return <LogProps {...props} forwardedRef={ref} />;
  4. }
  5. );

3,可以设置函数的 displayName 属性来包含被包裹组件的名称

  1. function logProps(Component) {
  2. class LogProps extends React.Component {
  3. // ...
  4. }
  5. function forwardRef(props, ref) {
  6. return <LogProps {...props} forwardedRef={ref} />;
  7. }
  8. // 在 DevTools 中为该组件提供一个更有用的显示名。
  9. // 例如 “ForwardRef(logProps(MyComponent))”
  10. const name = Component.displayName || Component.name;
  11. forwardRef.displayName = `logProps(${name})`;
  12. return React.forwardRef(forwardRef);
  13. }