react 随笔记录

1.jsx

jsx:是一个javaScript的语法扩展,在react中配合使用jsx,可以更好的表述界面,表示ui页面及页面的一些变量

const element = <h1>Hello, world!</h1>;

babel会把jsx转义成React.createElement()函数调用

const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

ps:由于 JSX 会编译为 React.createElement 调用形式,所以 React 库也必须包含在 JSX 代码作用域内。这就是每次编写的时候都需要在顶部import React from 'react' 的原因。

 

2.函数组件和类组件

// 函数组件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 类组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

函数组件和类组件的区别

Hook 避免了 class 需要的额外开支,像是创建类实例和在构造函数中绑定事件处理器的成本

符合语言习惯的代码在使用 Hook 时不需要很深的组件树嵌套。这个现象在使用高阶组件、render props、和 context 的代码库中非常普遍。组件树小了,React 的工作量也随之减少。

 

从类组件迁移到函数组件

  • constructor:函数组件不需要构造函数。你可以通过调用 useState 来初始化 state。如果计算的代价比较昂贵,你可以传一个函数给 useState
  • getDerivedStateFromProps:改为 在渲染时 安排一次更新。
  • shouldComponentUpdate:详见 React.memo.
  • render:这是函数组件体本身。
  • componentDidMountcomponentDidUpdatecomponentWillUnmountuseEffect Hook 可以表达所有这些(包括 不那么 常见 的场景)的组合。
  • getSnapshotBeforeUpdatecomponentDidCatch 以及 getDerivedStateFromError:目前还没有这些方法的 Hook 等价写法,但很快会被添加。

 

 

 

3.setState

setState的更新可能是异步的,所以当setState({})对于数据进行更新后,可能不会得到你想要的值,可能没有立即更新

setState在合成事件和钩子函数中表现为异步,在原生事件和setTimeout中表现为同步

出于性能考虑,setState()会把多次更新合并为一次提交

this.props,this.state可能会异步更新,不要根据他们的值来进行更新,可以通过接受函数的形式来进行更新

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});

// Correct
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

 

 

4.事件处理机制

(1)react 事件处理机制与原生html有所不同

// 原生
<button onclick="activateLasers()">
  Activate Lasers
</button>

// React采用驼峰命名
<button onClick={activateLasers}>
  Activate Lasers
</button>

 

(2)对于默认行为的禁用

// 原生事件:通过return false阻止默认行为
<a href="#" onclick="console.log('The link was clicked.'); return false">
  Click me
</a>

// react: 通过e.preventDefault()阻止默认行为
function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

 

类组件中绑定事件处理函数,需要注意this的绑定问题

// 1.通过bind手动对this进行绑定
this.handleClick = this.handleClick.bind(this);

// 2.绑定的函数为箭头函数
<button onClick={this.handleClick}>
        Click me
</button>
 handleClick = () => {
    console.log('this is:', this);
  }

// 3.在回调函数中使用箭头函数
// 这里可以传参e, (e) => this.handleClick(e),e是一个合成事件
<button onClick={() => this.handleClick()}>
        Click me
</button>

 

5.受控组件与非受控组件

受控组件:表单类的元素,使用react的state作为数据源,并通过state的控制来进行控制表单

使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。

 

非受控组件:表单数据通过dom节点来控制,可以使用ref来获得表单数据

受控组件和非受控组件的对比:

特征非受控组件受控组件
一次性价值检索(例如在提交时)
提交时验证
即时字段验证
有条件地禁用提交按钮
强制输入格式
一个数据的多个输入
动态输入

 

6.context

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。

context使用的目的是为了在多个组件中都能使用公共的一些props

通过context,可以避免通过中间组件来传递props

// 1.首先创建一个context(light为默认值)
const ThemeContext= React.createContext('light');

// 2.通过<ThemeContext.Provider>包裹传递给子组件
class App extends React.Component {
  render() {
    // 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
    // 无论多深,任何组件都能读取这个值。
    // 在这个例子中,我们将 “dark” 作为当前的值传递下去。
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

// 3.中间组件不用传递props
// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );

// 4.嵌套的孙子组件可以读取value:dark
class ThemedButton extends React.Component {
  // 指定 contextType 读取当前的 theme context。
  // React 会往上找到最近的 theme Provider,然后使用它的值。
  // 在这个例子中,当前的 theme 值为 “dark”。
  static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />;
  }
}

 

7.refs转发

// FancyButton 使用 React.forwardRef 来获取传递给它的 ref,然后转发到它渲染的 DOM button
// 第二个参数ref只在React.forwardRef中存在,常规函数组件和类组件不存在
const FancyButton = React.forwardRef((props, ref) => ( <button ref={ref} className="FancyButton"> {props.children} </button> )); // 你可以直接获取 DOM button 的 ref: // 通过React.createRef()创建ref,传递给FancyButton组件 // 再传递给子button const ref = React.createRef(); <FancyButton ref={ref}>Click me!</FancyButton>;

 refs提供了可以读取dom元素得一种方式

refs只能在类组件中使用,在函数组件中可以通过forwardRef进行使用

在函数组件中通过useRef声明refs

function CustomTextInput(props) {
  // 这里必须声明 textInput,这样 ref 才可以引用它
  const textInput = useRef(null);

  function handleClick() {
    textInput.current.focus();
  }

  return (
    <div>
      <input
        type="text"
        ref={textInput} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );
}

 

 

8.高阶组件

高阶组件:是一个参数为组件,返回值为组件的函数

const EnhancedComponent = higherOrderComponent(WrappedComponent);

es: redux中的connect

高阶组件不应该改变原有组件而是要使用组合的方式

高阶组件接受组件作为参数,然后返回一个增强性的组件。

注意事项:

1.不要在render中使用高阶组件

2.拷贝组件上的静态方法

3.refs不会被转发

 

9.生命周期函数

挂载阶段:

constructor()
static getDerivedStateFromProps()
render()
componentDidMount()

更新阶段:

static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()

卸载阶段:

componentWillUnmount()

componentDidMount(): 会在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,此处是实例化请求的好地方。

componentDidUpdate(): 会在更新后会被立即调用。首次渲染不会执行此方法。当组件更新后,可以在此处对 DOM 进行操作。如果你对更新前后的 props 进行了比较,也可以选择在此处进行网络请求。执行事件需要包含在条件语句中,否则会导致循环更新。

如果 shouldComponentUpdate() 返回值为 false,则不会调用 componentDidUpdate()

componentWillUnmount():会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等

shouldComponentUpdate():根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。默认行为是 state 每次发生变化组件都会重新渲染。

getDerivedStateFromProps():会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。

getSnapshotBeforeUpdate():在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数传递给 componentDidUpdate()

 

10.hooks

hooks使用规则:

  • 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用。
  • 只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用。

(1): useState()

useState 会返回一对值:当前状态和一个让你更新它的函数

  // 声明一个叫 “count” 的 state 变量。
// 初始值为count,通过setCount来进行更新
  const [count, setCount] = useState(0);

(2): useEffect()

useEffect 就是一个 Effect Hook,给函数组件增加了操作副作用的能力.

它跟 class 组件中的 componentDidMountcomponentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个 API

  // 相当于 componentDidMount 和 componentDidUpdate:
  useEffect(() => {
    // 使用浏览器的 API 更新页面标题
    document.title = `You clicked ${count} times`;
  });

// 通过返回值return 来实现 componentWillUnmount 清除作用
 useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

可以只在更新的时候使用useEffect吗?

通过useRef()来进行更新

可以 使用一个可变的 ref 手动存储一个布尔值来表示是首次渲染还是后续渲染,然后在你的 effect 中检查这个标识。 

const isInitialMount = useRef(true);

useEffect(() => {
  if (isInitialMount.current) {
     isInitialMount.current = false;
  } else {
      // Your useEffect code here to be run on update
  }
});

 

 

(3): 自定义hooks

 

(4): useContext

 

const value = useContext(MyContext);

接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定。

const themes = {
  light: {
    foreground: "#000000",
    background: "#eeeeee"
  },
  dark: {
    foreground: "#ffffff",
    background: "#222222"
  }
};

const ThemeContext = React.createContext(themes.light);

function App() {
  return (
    <ThemeContext.Provider value={themes.dark}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
// 类组件中通过
static contextType = ThemeContext;
return ( <button style={{ background: theme.background, color: theme.foreground }}> I am styled by theme context! </button>  ); }

 

posted @ 2022-07-06 18:17  千亿昔  阅读(21)  评论(0编辑  收藏  举报