Apakah konteks dalam tindak balas

WBOY
Lepaskan: 2022-06-28 10:55:37
asal
2471 orang telah melayarinya

Secara tindak balas, konteks ialah kaedah untuk memindahkan data antara pepohon komponen tanpa menambah props secara manual untuk setiap lapisan konteks menyediakan cara untuk berkongsi nilai yang ditentukan antara komponen, dan Tidak perlu lulus secara eksplisit prop melalui pokok komponen.

Apakah konteks dalam tindak balas

Persekitaran pengendalian tutorial ini: Sistem Windows 10, bertindak balas versi 17.0.1, komputer Dell G3.

Apakah konteks dalam tindak balas

Konteks menyediakan kaedah untuk memindahkan data antara pepohon komponen tanpa menambah prop pada setiap lapisan komponen secara manual. Dalam aplikasi React biasa, data dihantar dari atas ke bawah (daripada ibu bapa kepada anak) melalui prop, tetapi pendekatan ini amat menyusahkan untuk jenis sifat tertentu (cth. pilihan tempat, tema UI), sifat ini diperlukan oleh banyak komponen dalam permohonan itu. Konteks menyediakan cara untuk berkongsi nilai sedemikian antara komponen tanpa perlu secara eksplisit menghantar prop melalui setiap peringkat pepohon komponen.

Bilakah konteks digunakan?

Konteks direka bentuk untuk berkongsi data yang "global" kepada pepohon komponen, seperti pengguna, tema atau bahasa pilihan yang kini disahkan. Contohnya, dalam kod berikut, kami melaraskan gaya komponen butang secara manual melalui atribut "tema"

class App extends React.Component {
  render() {
    return <Toolbar theme="dark" />;
  }
}

function Toolbar(props) {
  // Toolbar 组件接受一个额外的“theme”属性,然后传递给 ThemedButton 组件。
  // 如果应用中每一个单独的按钮都需要知道 theme 的值,这会是件很麻烦的事,
  // 因为必须将这个值层层传递所有组件。
  return (
    <p>
      <ThemedButton theme={props.theme} />
    </p>
  );
}

class ThemedButton extends React.Component {
  render() {
    return <Button theme={this.props.theme} />;
  }
}

// 通过props传递:App -> Toolbar -> ThemedButton
// 如果嵌套很深,那么需要逐层传递props,即使中间不需要该props,显得很繁琐
Salin selepas log masuk

Menggunakan konteks, kita boleh mengelak daripada menghantar prop melalui elemen perantaraan

// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的 theme 创建一个 context("light"为默认值)。
const ThemeContext = React.createContext('light');
class App extends React.Component {
  render() {
    // 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
    // 无论多深,任何组件都能读取这个值。
    // 在这个例子中,我们将 “dark” 作为当前的值传递下去。
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

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

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

Pengenalan API

React.createContext

const MyContext = React.createContext(defaultValue);
Salin selepas log masuk

Buat objek Konteks. Apabila React memaparkan komponen yang melanggan objek Konteks ini, komponen akan membaca nilai konteks semasa daripada Provider padanan yang paling hampir dengan dirinya dalam pepohon komponen.

Hanya apabila tiada Penyedia yang sepadan dalam pepohon tempat komponen terletak, parameter defaultValuenya akan berkuat kuasa. Ini membantu dalam menguji komponen tanpa membungkusnya dengan Pembekal. Nota: Apabila undefined dihantar kepada nilai Pembekal, defaultValue komponen pengguna tidak akan berkuat kuasa.

Context.Provider

<MyContext.Provider value={/* 某个值 */}>
Salin selepas log masuk

Setiap objek Konteks mengembalikan komponen Reaksi Penyedia, yang membolehkan komponen yang menggunakan melanggan perubahan dalam konteks.

Pembekal menerima atribut value dan menyerahkannya kepada komponen pengguna. Pembekal boleh mempunyai hubungan yang sepadan dengan berbilang komponen pengguna. Berbilang Pembekal juga boleh digunakan bersarang, dan lapisan dalam akan menimpa data lapisan luar.

Apabila nilai value Pembekal berubah, semua komponen penggunaan di dalamnya akan dipaparkan semula. Baik Pembekal mahupun komponen pengguna dalamannya tidak tertakluk kepada fungsi shouldComponentUpdate, jadi komponen pengguna boleh dikemas kini walaupun komponen moyangnya keluar daripada kemas kini.

Class.contextType

Atribut contextType yang dipasang pada kelas akan ditugaskan semula kepada objek Konteks yang dicipta oleh React.createContext(). Ini membolehkan anda menggunakan this.context untuk menggunakan nilai pada Konteks terkini. Anda boleh mengaksesnya dalam mana-mana kitaran hayat, termasuk fungsi pemaparan

import MyContext from './MyContext';

class MyClass extends React.Component {
  componentDidMount() {
    let value = this.context;
    /* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* 基于 MyContext 组件的值进行渲染 */
  }
  // 或者如上边例子一样使用 static contextType = MyContext;
}
MyClass.contextType = MyContext;
Salin selepas log masuk

Context.Consumer

import MyContext from './MyContext';

function ToolList() {
  return (
    <MyContext.Consumer
      {value => /* 基于 context 值进行渲染*/}
    </MyContext.Consumer>
  )
}
Salin selepas log masuk

Di sini, komponen React juga boleh melanggan perubahan konteks. Ini membolehkan anda melanggan konteks dalam komponen berfungsi.

Ini memerlukan fungsi sebagai seorang kanak-kanak. Fungsi ini menerima nilai konteks semasa dan mengembalikan nod React. Nilai value yang dihantar kepada fungsi adalah bersamaan dengan nilai value yang disediakan oleh Penyedia yang paling hampir dengan konteks ini di atas pepohon komponen. Jika tiada Pembekal yang sepadan, parameter value adalah bersamaan dengan createContext() yang dihantar kepada defaultValue.

Context.displayName

objek konteks menerima sifat bernama displayName rentetan jenis. React DevTools menggunakan rentetan ini untuk menentukan konteks yang hendak dipaparkan.

Komponen berikut akan dipaparkan sebagai MyDisplayName dalam DevTools

const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';

<MyContext.Provider>   // "MyDisplayName.Provider" 在 DevTools 中
<MyContext.Consumer>   // "MyDisplayName.Consumer" 在 DevTools 中
Salin selepas log masuk

Contoh

Konteks Dinamik

Untuk contoh tema di atas, gunakan nilai dinamik untuk penggunaan yang lebih kompleks

tema-konteks.js

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

export const ThemeContext = React.createContext(themes.dark);   // 该处为默认值
Salin selepas log masuk

button-bertema.js

import { ThemeContext } from './theme-context';

class ThemedButton extends React.Component {
  render() {
    let props = this.props;
    // 获取到ThemeContext中的默认值
    let theme = this.context;
    return (
      <button
        {...props}
        style={{backgroundColor: theme.background}}
      />
    );
  }
  // static contextType = ThemeContext;
}
ThemedButton.contextType = ThemeContext;

export default ThemedButton;
Salin selepas log masuk

app.js

import { ThemeContext, themes } from './theme-context';
import ThemedButton from './themed-button';

// 一个使用 ThemedButton 的中间组件
function Toolbar(props) {
  return (
    <ThemedButton onClick={props.changeTheme}>
      Change Theme
    </ThemedButton>
  );
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      theme: themes.light,
    };

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

  render() {
    // 在 ThemeProvider 内部的 ThemedButton 按钮组件使用 state 中的 theme 值,
    // 而外部的组件使用默认的 theme 值
    return (
      <Page>
        <ThemeContext.Provider value={this.state.theme}>
          <Toolbar changeTheme={this.toggleTheme} />
        </ThemeContext.Provider>
        <Section>
          <ThemedButton />
        </Section>
      </Page>
    );
  }
}

ReactDOM.render(<App />, document.root);

// 使用ThemeContext.Provider包裹的组件,可以消费到ThemeContext中的value
// 即Toolbar、ThemedButton中都可以使用this.context来获取到value
// 注意观察,更新state的方法是通过props向下传递,由子孙组件触发更新,下面会讲到通过context的方式传递更新函数
Salin selepas log masuk

在嵌套组件中更新 Context

在上面的例子中,我们通过 props 的方式向下传递一个更新函数,从而改变 App 中 themes 的值。我们知道,从一个在组件树中嵌套很深的组件中更新 context 是很有必要的。在这种场景下,你可以通过 context 传递一个函数,使得 consumers 组件更新 context

theme-context.js

// 确保传递给 createContext 的默认值数据结构是调用的组件(consumers)所能匹配的!
export const ThemeContext = React.createContext({
  theme: themes.dark,
  toggleTheme: () => {},   // 定义更新主题的方法,向下传递
});
Salin selepas log masuk

theme-toggler-button.js

import { ThemeContext } from './theme-context';

function ThemeTogglerButton() {
  // Theme Toggler 按钮不仅仅只获取 theme 值,它也从 context 中获取到一个 toggleTheme 函数(下面app.js部分)
  return (
    <ThemeContext.Consumer>
      {({theme, toggleTheme}) => (
        <button onClick={toggleTheme} style={{backgroundColor: theme.background}}>
          Toggle Theme
        </button>
      )}
    </ThemeContext.Consumer>
  );
}

export default ThemeTogglerButton;
Salin selepas log masuk

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,   // 定义更新函数,通过context方式向下传递
    };
  }

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

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

ReactDOM.render(<App />, document.root);
Salin selepas log masuk

消费多个 Context

为了确保 context 快速进行重渲染,React 需要使每一个 consumers 组件的 context 在组件树中成为一个单独的节点

// Theme context,默认的 theme 是 "light" 值
const ThemeContext = React.createContext('light');

// 用户登录 context
const UserContext = React.createContext({
  name: 'Guest',
});

class App extends React.Component {
  render() {
    const { signedInUser, theme } = this.props;

    // 提供初始 context 值的 App 组件
    return (
      <ThemeContext.Provider value={theme}>
        <UserContext.Provider value={signedInUser}>
          <Layout />
        </UserContext.Provider>
      </ThemeContext.Provider>
    );
  }
}

function Layout() {
  return (
    <p>
      <Sidebar />
      <Content />
    </p>
  );
}

// 一个组件可能会消费多个 context
function Content() {
  return (
    <ThemeContext.Consumer>
      {theme => (
        <UserContext.Consumer>
          {user => (
            <ProfilePage user={user} theme={theme} />
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>
  );
}
Salin selepas log masuk

如果两个或者更多的 context 值经常被一起使用,那你可能要考虑一下另外创建你自己的渲染组件,以提供这些值。

注意事项

因为 context 会使用参考标识(reference identity)来决定何时进行渲染,这里可能会有一些陷阱,当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。举个例子,当每一次 Provider 重渲染时,以下的代码会重渲染所有下面的 consumers 组件,因为 value 属性总是被赋值为新的对象

class App extends React.Component {
  render() {
    return (
      <MyContext.Provider value={{something: &#39;something&#39;}}>
        <Toolbar />
      </MyContext.Provider>
    );
  }
}
Salin selepas log masuk

为了防止这种情况,将 value 状态提升到父节点的 state 里

class App extends React.Component {
  constructor(props) {
    super(props);
    // 多次渲染,state 会被保留,当value不变时,下面的 consumers 组件不会重新渲染 
    this.state = {
      value: {something: 'something'},
    };
  }

  render() {
    return (
      <Provider value={this.state.value}>
        <Toolbar />
      </Provider>
    );
  }
}
Salin selepas log masuk

【相关推荐:javascript视频教程web前端

Atas ialah kandungan terperinci Apakah konteks dalam tindak balas. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan