我是靠谱客的博主 负责蓝天,最近开发中收集的这篇文章主要介绍react 笔记,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

react 笔记

React 用于构建用户界面的 js库。使用 React 可以将一些简短、独立的代码片段组合成复杂的 UI 界面,这些代码片段被称作“组件”。
class组件
通过 class 定义组件,继承React.Component,定义render方法使用jsx语法书写结构,返回一个React元素。
访问属性: this.props
添加点击事件:onClick={fn}
定义状态:在constructor 初始化 this.state
修改状态:this.setState,只会指定属性的值。
多组件共享状态:在父组件中定义状态,然后通过props传递给子组件。click函数同样。
不可变性:不直接修改数据有利于追踪数据变化,实现回退功能。确定组件渲染时间。

class ShoppingList extends React.Component {
  constructor(props) {
   super(props);
    this.state = {
      value: null,
    };
  }

  render() {
    return (
      <div className="shopping-list">
        <h1>Shopping List for {this.props.name}</h1>
        <button
          className="square"
          onClick={() => this.setState({value: 'X'})}>
	        {this.state.value}
	    </button>
        <ul>
          <li>Instagram</li>
          <li>WhatsApp</li>
          <li>Oculus</li>
        </ul>
      </div>
    );
  }
}

函数组件
接受props属性,返回一个React元素
访问属性: props

function Square(props) {
  return (
    <button className="square" onClick={props.onClick}>
      {props.value}
    </button>
  );
}

key的作用
列表渲染时,判断如果现在存在,但之前不存在这个key会创建这个元素;如果现在不存在,但之前存在会删除这个元素。如果key发生了变化。会删除之前的然后新创建一个。
数组的索引不适合作为key。排序、新增、删除操作时,数组的索引会变动。导致key变动,导致渲染问题。

JSX语法

1. 将变量用大括号包裹。
const element = <h1>Hello, {name}</h1>;
2. 执行函数表达式
function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}
const element = <h1>Hello, {formatName(user)}</h1>;
3. 访问属性
const element = <img src={user.avatarUrl}></img>;
4. 添加属性名,采用驼峰命名
const element = <h1 className="hell">Hello</h1>;

5. 闭合标签 />
const element = <img src={user.avatarUrl} />;

6. 防止XSS注入攻击,title在被渲染之前会被自动转义。
const title = response.potentiallyMaliciousInput;
const element = <h1>{title}</h1>;

8. 

元素渲染


1. 这种我们称之为元素,是一个普通对象。是不可变的。跟浏览器的元素不一样。需要执行ReactDOM.render渲染
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
2. 元素和组件不一样。
3. 自定义组件也可以做元素。属性会被转为对象传递给组件。
const element = <Welcome name="Sara" />;
4. 元素渲染流程
首先通过ReacDOM.render渲染元素element 。然后调用Welcome组件,传入属性。Welcome组件返回render内容。React DOM更新内容。
5. 

组件


1. 组件名必须大写。
2. 提取组件。
function Avatar(props) {
  return (
    <img className="Avatar"
      src={props.user.avatarUrl}
      alt={props.user.name}
    />
  );
}
3. 组合组件。
function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}
4. props只读。
不能修改自身的 props
5. 通过State让组件自我更新。
在class组件的constructor中初始化state。在render中调用。在componentDidMount中添加定时器修改。在componentWillUnmount中删除定时器。
调用顺序:首先React 调用 Clock组件的构造函数。初始化state。调用组件的render。更新DOM。执行生命周期函数ComponentDidMount,设置计时器。浏览器执行定时器。调用setState。React得知state变化。重新调用render。一旦Clock组件移除。执行生命周期函数componentWillUnmount。
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }
  
  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
6. state
不可直接修改。只能通过setState调用。
State 的更新可能是异步的。
setState不能依赖state修改
this.setState({
  counter: this.state.counter + this.props.increment,
});
可以通过函数
// Correct
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));
State 的更新会被合并,合并是浅合并,不会修改其他state
this.setState({
  posts: response.posts
});
this.setState({
 comments: response.comments
});
7. 数据自上向下流动。
8. 事件处理
驼峰命名
<button onClick={activateLasers}>Activate Lasers</button>
不能通过返回 false 的方式阻止默认行为
在class组件中使用,需要在constructor中绑定this或者使用箭头函数。onClick={() => this.handleClick()}
class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // 为了在回调中使用 `this`,这个绑定是必不可少的
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

ReactDOM.render(
  <Toggle />,
  document.getElementById('root')
);
传递参数
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
9. 条件渲染。
通过if
function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;
  if (isLoggedIn) {
    return <UserGreeting />;
  }
  return <GuestGreeting />;
}
通过与运算符 &&
isLoggedIn 为true才会渲染。为false会被忽略。
function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;
  return isLoggedIn && <UserGreeting />;
}
三目运算符
render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
    </div>
  );
}
阻止条件渲染
render 方法直接返回 null
10. 列表渲染
function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li key={number.toString()}>
      {number}
    </li>
  );
  return (
    <ul>{listItems}</ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);
内联map
function NumberList(props) {
  const numbers = props.numbers;
  return (
    <ul>
      {numbers.map((number) =>
        <ListItem key={number.toString()}
                  value={number} />
      )}
    </ul>
  );
}
11. 表单
受控组件:state作为数据源。通过setState更新。
同类标签input、textarea、select
<input type="text" value={this.state.value} onChange={this.handleChange} />
handleChange(event) {
 this.setState({value: event.target.value});
}
非受控组件
真实数据储存在 DOM 节点
<input type="text" ref={this.input} />
alert('A name was submitted: ' + this.input.current.value);
Formik
import React from 'react';
import {useFormik } from 'formik';

const SignupForm = () => {
  // Note that we have to initialize ALL of fields with values. These
  // could come from props, but since we don’t want to prefill this form,
  // we just use an empty string. If we don’t do this, React will yell
  // at us.
  const formik = useFormik({
    initialValues: {
      firstName: '',
      lastName: '',
      email: '',
    },
    onSubmit: values => {
      alert(JSON.stringify(values, null, 2));
    },
  });
  return (
    <form onSubmit={formik.handleSubmit}>
      <label htmlFor="firstName">First Name</label>
      <input
        id="firstName"
        name="firstName"
        type="text"
        onChange={formik.handleChange}
        value={formik.values.firstName}
      />

      <label htmlFor="lastName">Last Name</label>
      <input
        id="lastName"
        name="lastName"
        type="text"
        onChange={formik.handleChange}
        value={formik.values.lastName}
      />

      <label htmlFor="email">Email Address</label>
      <input
        id="email"
        name="email"
        type="email"
        onChange={formik.handleChange}
        value={formik.values.email}
      />

      <button type="submit">Submit</button>
    </form>
  );
};
12. 组合与继承
使用children,子节点内容由父节点确定
function FancyBorder(props) {
  return (
    <div className={'FancyBorder FancyBorder-' + props.color}>
      {props.children}
    </div>
  );
}
通过属性left、right传递不同的组件。
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 />
      } />
  );
}
如果你想要在组件间复用非 UI 的功能,我们建议将其提取为一个单独的 JavaScript 模块,如函数、对象或者类。组件可以直接引入(import)而无需通过 extend 继承它们。
13. React开发步骤
首先根据设计UI划分组件层级(根据单一功能原则把组件当做一个函数或对象,如果它负责更多的功能,应该拆分成更小的组件);
然后用React 创建一个静态版本(自上而下比较方便,但自下而上适合大型项目同时编写测试更简单)
然后确定需要用到的最小且完整的state
然后确定state位置(所有需要用到该state的共同父级,如果没有就创建一个)。
最后添加反向数据流(较低层组件通过事件更新较高层级组件的state)

高级功能


1. 代码分割
避免代码体积过大导致加载时间过长;避免加载用户不需要的代码。
动态 import()
import { add } from './math';
console.log(add(16, 26));
例如
import("./math").then(math => {
  console.log(math.add(16, 26));
});
React.lazy
import OtherComponent from './OtherComponent';
例如
const OtherComponent = React.lazy(() => import('./OtherComponent'));
然后应在 Suspense 组件中渲染 lazy 组件
import React, { Suspense } from 'react';

const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <OtherComponent />
      </Suspense>
    </div>
  );
}
基于路由的代码分割
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Switch>
        <Route exact path="/" component={Home}/>
        <Route path="/about" component={About}/>
      </Switch>
    </Suspense>
  </Router>
);
React.lazy 目前只支持默认导出(default exports)

2. Context
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法
适合管理locale,theme,或者一些缓存数据	
通过React.createContext创建context对象
通过ThemeContext.Provider并设置value向下传递context内容
多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。
当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。
** 设置MyClass.contextType = MyContext; class组件可以直接通过 this.context消费context
** 设置MyContext.Consumer消费
<MyContext.Consumer>
  {value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>

不利于组件的复用(替代方案使用组件组合,将底层组件整个传递下去,但会使组件更复杂)
const ThemeContext = React.createContext('light');
class App extends React.Component {
  render() {
    // 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
    // 无论多深,任何组件都能读取这个值。
    // 在这个例子中,我们将 “dark” 作为当前的值传递下去。
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}
动态 Context
将context内容设置为一个对象。
theme-context.js
export const themes = {
  light: {
    foreground: '#000000',
    background: '#eeeeee',
  },
  dark: {
    foreground: '#ffffff',
    background: '#222222',
  },
};

export const ThemeContext = React.createContext(
  themes.dark // 默认值
);
themed-button.js
import {ThemeContext} from './theme-context';

class ThemedButton extends React.Component {
  render() {
    let props = this.props;
    let theme = this.context;
    return (
      <button
        {...props}
        style={{backgroundColor: theme.background}}
      />
    );
  }
}
ThemedButton.contextType = ThemeContext;

export default ThemedButton;

app.js
import {ThemeContext, themes} from './theme-context';
import ThemeTogglerButton from './theme-toggler-button';

class App extends React.Component {
  constructor(props) {
    super(props);

    this.toggleTheme = () => {
      this.setState(state => ({
        theme:
          state.theme === themes.dark
            ? themes.light
            : themes.dark,
      }));
    };

    // State 也包含了更新函数,因此它会被传递进 context provider。
    this.state = {
      theme: themes.light,
      toggleTheme: this.toggleTheme,
    };
  }

  render() {
    // 整个 state 都被传递进 provider
    return (
      <ThemeContext.Provider value={this.state}>
        <Content />
      </ThemeContext.Provider>
    );
  }
}

function Content() {
  return (
    <div>
      <ThemeTogglerButton />
    </div>
  );
}

ReactDOM.render(<App />, document.root);
消费多个 Context
// 一个组件可能会消费多个 context
function Content() {
  return (
    <ThemeContext.Consumer>
      {theme => (
        <UserContext.Consumer>
          {user => (
            <ProfilePage user={user} theme={theme} />
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>
  );
}
3. 错误边界
错误边界是一个React 组件,捕获子组件树上的js错误,并渲染备用UI
实现方式,在class组件上定义 static getDerivedStateFromError或componentDidCatch方法
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // 更新 state 使下一次渲染能够显示降级后的 UI
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // 你同样可以将错误日志上报给服务器
    logErrorToMyService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // 你可以自定义降级后的 UI 并渲染
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}
<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>
4. Refs 转发
将ref通过组件传递到子组件,主要解决类似FancyButton 这样封装单个控件的高可复用组件可以直接对button进行操作。一般不推荐使用。
React.createRef()创建ref;React.forwardRef接收ref
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>;

5. Fragments
一个组件返回多个元素
无需向 DOM 添加额外节点
render() {
  return (
    <React.Fragment>
      <ChildA />
      <ChildB />
      <ChildC />
    </React.Fragment>
  );
}
短语法
class Columns extends React.Component {
  render() {
    return (
      <>
        <td>Hello</td>
        <td>World</td>
      </>
    );
  }
}
6. 高阶组件
高阶组件是参数为组件,返回值为新组件的函数
组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件
使用 HOC 解决横切关注点问题
不要改变原始组件。
不要将不相关的 props 传递给被包裹的组件
包装显示名称以便轻松调试
不要在 render 方法中使用 HOC,这将导致子树每次渲染都会进行卸载,和重新挂载的操作!
// 此函数接收一个组件...
function withSubscription(WrappedComponent, selectData) {
  // ...并返回另一个组件...
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.handleChange = this.handleChange.bind(this);
      this.state = {
        data: selectData(DataSource, props)
      };
    }

    componentDidMount() {
      // ...负责订阅相关的操作...
      DataSource.addChangeListener(this.handleChange);
    }

    componentWillUnmount() {
      DataSource.removeChangeListener(this.handleChange);
    }

    handleChange() {
      this.setState({
        data: selectData(DataSource, this.props)
      });
    }

    render() {
      // ... 并使用新数据渲染被包装的组件!
      // 请注意,我们可能还会传递其他属性
      return <WrappedComponent data={this.state.data} {...this.props} />;
    }
  };
}

const CommentListWithSubscription = withSubscription(
  CommentList,
  (DataSource) => DataSource.getComments()
);

7. 深入JSX
在运行时选择组件类型
import React from 'react';
import { PhotoStory, VideoStory } from './stories';

const components = {
  photo: PhotoStory,
  video: VideoStory
};

function Story(props) {
  // 正确!JSX 类型可以是大写字母开头的变量。
  const SpecificStory = components[props.storyType];
  return <SpecificStory story={props.story} />;
}
属性展开
function App2() {
  const props = {firstName: 'Ben', lastName: 'Hector'};
  return <Greeting {...props} />;
}
函数作为子元素
function ListOfTenThings() {
  return (
    <Repeat numTimes={10}>
      {(index) => <div key={index}>This is item {index} in the list</div>}
    </Repeat>
  );
}
布尔类型、Null 以及 Undefined 将会忽略

8. Render
使用 Render Props 来解决横切关注点
这个组件的问题无法复用;state要复用的话一定要提取出来。
class MouseTracker extends React.Component {
  constructor(props) {
    super(props);
    this.handleMouseMove = this.handleMouseMove.bind(this);
    this.state = { x: 0, y: 0 };
  }

  handleMouseMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }

  render() {
    return (
      <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
        <h1>移动鼠标!</h1>
        <p>当前的鼠标位置是 ({this.state.x}, {this.state.y})</p>
      </div>
    );
  }
}
解决方案
Mouse组件提供坐标;Cat组件可以被轻松替换成其他组件。Mouse和Cat都有使用相同的state,是由Mouse传递出来的。Cat在Mouse内被渲染,但是却在Mouse外确定。因此Cat组件很灵活。
这里的render名字只是名字,也可以是children等之类的名称
class Cat extends React.Component {
  render() {
    const mouse = this.props.mouse;
    return (
      <img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
    );
  }
}

class Mouse extends React.Component {
  constructor(props) {
    super(props);
    this.handleMouseMove = this.handleMouseMove.bind(this);
    this.state = { x: 0, y: 0 };
  }

  handleMouseMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }

  render() {
    return (
      <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>

        {/*
          Instead of providing a static representation of what <Mouse> renders,
          use the `render` prop to dynamically determine what to render.
        */}
        {this.props.render(this.state)}
      </div>
    );
  }
}

class MouseTracker extends React.Component {
  render() {
    return (
      <div>
        <h1>移动鼠标!</h1>
        <Mouse render={mouse => (
          <Cat mouse={mouse} />
        )}/>
      </div>
    );
  }
}

9. StrictMode 
识别不安全的生命周期
关于使用过时字符串 ref API 的警告
关于使用废弃的 findDOMNode 方法的警告
检测意外的副作用
检测过时的 context API

HOOK
不编写 class 的情况下使用 state
解决在组件之间复用状态逻辑很难,Hook 将组件中相互关联的部分拆分成更小的函数
解决难以理解的 class
只能在函数最外层调用 Hook
只能在 React 的函数组件中调用 Hook
Hook 是特殊的函数,让你可以“钩入”React的特性。


1. State Hook
在函数组件中调用 useState,参数是设置state的初始值,返回当前 state 以及更新 state 的函数
state可以是多个也可以对象等类型
import React, { useState } from 'react';

function Example() {
  // 声明一个叫 “count” 的 state 变量。
  const [count, setCount] = useState(0);
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
2. Effect Hook
在函数组件中调用 useEffect , 组件更新时自动执行。可以通过返回一个函数清除该副作用。
function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
3. 自定义 Hook
通过useState 和 useEffect
复用逻辑而不是状态
import React, { useState, useEffect } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

4. useContext
const locale = useContext(LocaleContext); 不嵌套组件就可以实现context的订阅
const theme = useContext(ThemeContext);

const [todos, dispatch] = useReducer(todosReducer);通过 reducer 来管理组件本地的复杂 state。

5. 比较class组件
class组件中 的副作用放在 componentDidMount 或者 componentDidUpdate 中。
useEffect 直接执行副作用,可以在useEffect 里面执行dom 访问。数据获取。可以直接访问 state、props
componentDidMount() {
 document.title = `You clicked ${this.state.count} times`;
}
componentDidUpdate() {
 document.title = `You clicked ${this.state.count} times`;
}
useEffect(() => {
 document.title = `You clicked ${count} times`;
});
副作用清除。class组件是在componentWillUnmount中。
useEffect 直接返回清除副作用的函数。
class组件在处理订阅时还需要在组件更新时执行清除然后订阅的操作;useEffect每次更新都会自动清除然后添加订阅。
性能优化:useEffect传入第二个参数,只有当参数数组里面的某个state变化才执行副作用。
useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新
如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])作为第二个参数。

最后

以上就是负责蓝天为你收集整理的react 笔记的全部内容,希望文章能够帮你解决react 笔记所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(73)

评论列表共有 0 条评论

立即
投稿
返回
顶部