Làm cách nào để cập nhật React Context từ bên trong một thành phần con?


100

Tôi có cài đặt ngôn ngữ trong ngữ cảnh như bên dưới

class LanguageProvider extends Component {
  static childContextTypes = {
    langConfig: PropTypes.object,
  };

  getChildContext() {
    return { langConfig: 'en' };
  }

  render() {
    return this.props.children;
  }
}

export default LanguageProvider;

Mã ứng dụng của tôi sẽ giống như bên dưới

<LanguageProvider>
  <App>
    <MyPage />
  </App>
</LanguageProvider>

Trang của tôi đang có một thành phần để chuyển đổi ngôn ngữ

<MyPage>
  <LanguageSwitcher/>
</MyPage>

LanguageSwitcher trong điều này MyPagecần cập nhật ngữ cảnh để thay đổi ngôn ngữ thành 'jp' như bên dưới

class LanguageSwitcher extends Component {
  static contextTypes = {
    langConfig: PropTypes.object,
  };

  updateLanguage() {
    //Here I need to update the langConfig to 'jp' 
  }

  render() {
    return <button onClick={this.updateLanguage}>Change Language</button>;
  }
}

export default LanguageSwitcher;

Làm cách nào để cập nhật ngữ cảnh từ bên trong thành phần LanguageSwitcher?


Bạn đã đọc chưa? facebook.github.io/react/docs/context.html#updating-context Có lẽ đây là thứ gì đó phù hợp hơn cho trạng thái không phải ngữ cảnh
azium

@azium Có .. Trong tài liệu đó, ngữ cảnh được cập nhật từ chính thành phần hoặc có liên kết blog được thêm vào tài liệu chứa ngữ cảnh được chuyển làm đạo cụ cho trình cung cấp ngữ cảnh Tôi cần cập nhật nó từ thành phần con
mshameer

Uhh không, tài liệu nói không sử dụng ngữ cảnh nếu bạn cần cập nhật nó. Chính xác là "Đừng làm điều đó". Tôi sẽ nhắc lại, sử dụng nhà nước không bối cảnh
azium

2
@LondonRob bạn đang tìm kiếm loại câu trả lời chính tắc nào? IMO nội dung của tài liệu có vẻ tốt đối với tôi. Nếu bạn muốn thiết lập ngữ cảnh trong một đứa trẻ, chỉ cần tạo một bộ cài đặt trong thành phần của nhà cung cấp và chuyển nó cho người tiêu dùng trẻ em. Sau đó, gọi setter đó trong người tiêu dùng trẻ em và đặt nó thành bất kỳ dữ liệu nào nằm trong người dùng trẻ em. Vẫn theo đuổi ý tưởng nâng cao dữ liệu của React.
Andrew Li,

2
@azium chỉ là lời chúc mừng cho những người khác đọc nhận xét này suốt những năm sau đó. Cập nhật ngữ cảnh từ thành phần con hiện được hỗ trợ và khá đơn giản: hyp.is/FiP3mG6fEeqJiOfWzfKpgw/reactjs.org/docs/context.html
lustig

Câu trả lời:


228

Sử dụng móc

Hooks đã được giới thiệu trong 16.8.0 vì vậy mã sau đây yêu cầu phiên bản tối thiểu là 16.8.0 (cuộn xuống để xem ví dụ về thành phần lớp). CodeSandbox Demo

1. Đặt trạng thái gốc cho ngữ cảnh động

Thứ nhất, để có một ngữ cảnh động có thể được chuyển cho người tiêu dùng, tôi sẽ sử dụng trạng thái của phụ huynh. Điều này đảm bảo rằng tôi sẽ có một nguồn sự thật duy nhất. Ví dụ: Ứng dụng mẹ của tôi sẽ trông như thế này:

const App = () => {
  const [language, setLanguage] = useState("en");
  const value = { language, setLanguage };

  return (
    ...
  );
};

Các languageđược lưu trữ trong tiểu bang. Chúng ta sẽ chuyển cả hai languagevà hàm setter setLanguagequa ngữ cảnh sau.

2. Tạo bối cảnh

Tiếp theo, tôi tạo một ngữ cảnh ngôn ngữ như sau:

// set the defaults
const LanguageContext = React.createContext({
  language: "en",
  setLanguage: () => {}
});

Ở đây tôi đang đặt các giá trị mặc định cho language('en') và một setLanguagehàm sẽ được nhà cung cấp ngữ cảnh gửi đến (các) người tiêu dùng. Đây chỉ là những giá trị mặc định và tôi sẽ cung cấp các giá trị của chúng khi sử dụng thành phần nhà cung cấp trong phần mẹ App.

Lưu ý: LanguageContextvẫn giữ nguyên cho dù bạn

3. Tạo khách hàng bối cảnh

Để có bộ chuyển đổi ngôn ngữ đặt ngôn ngữ, nó phải có quyền truy cập vào chức năng bộ chuyển đổi ngôn ngữ thông qua ngữ cảnh. Nó có thể trông giống như sau:

const LanguageSwitcher = () => {
  const { language, setLanguage } = useContext(LanguageContext);
  return (
    <button onClick={() => setLanguage("jp")}>
      Switch Language (Current: {language})
    </button>
  );
};

Ở đây tôi chỉ đặt ngôn ngữ thành 'jp' nhưng bạn có thể có logic của riêng mình để đặt ngôn ngữ cho điều này.

4. Bao bọc người tiêu dùng trong một nhà cung cấp

Bây giờ tôi sẽ hiển thị thành phần trình chuyển đổi ngôn ngữ của mình trong một LanguageContext.Providervà chuyển các giá trị phải được gửi qua ngữ cảnh đến bất kỳ cấp nào sâu hơn. Đây là cách bố mẹ tôi Apptrông như thế nào:

const App = () => {
  const [language, setLanguage] = useState("en");
  const value = { language, setLanguage };

  return (
    <LanguageContext.Provider value={value}>
      <h2>Current Language: {language}</h2>
      <p>Click button to change to jp</p>
      <div>
        {/* Can be nested */}
        <LanguageSwitcher />
      </div>
    </LanguageContext.Provider>
  );
};

Bây giờ, bất cứ khi nào trình chuyển đổi ngôn ngữ được nhấp vào, nó sẽ tự động cập nhật ngữ cảnh.

CodeSandbox Demo

Sử dụng các thành phần lớp

API ngữ cảnh mới nhất đã được giới thiệu trong React 16.3, cung cấp một cách tuyệt vời để có một ngữ cảnh động. Đoạn mã sau đây yêu cầu phiên bản tối thiểu là 16.3.0. CodeSandbox Demo

1. Đặt trạng thái gốc cho ngữ cảnh động

Thứ nhất, để có một ngữ cảnh động có thể được chuyển cho người tiêu dùng, tôi sẽ sử dụng trạng thái của phụ huynh. Điều này đảm bảo rằng tôi sẽ có một nguồn sự thật duy nhất. Ví dụ: Ứng dụng mẹ của tôi sẽ trông như thế này:

class App extends Component {
  setLanguage = language => {
    this.setState({ language });
  };

  state = {
    language: "en",
    setLanguage: this.setLanguage
  };

  ...
}

languageđược lưu trữ trong trạng thái cùng với một phương thức thiết lập ngôn ngữ mà bạn có thể giữ bên ngoài cây trạng thái.

2. Tạo bối cảnh

Tiếp theo, tôi tạo một ngữ cảnh ngôn ngữ như sau:

// set the defaults
const LanguageContext = React.createContext({
  language: "en",
  setLanguage: () => {}
});

Ở đây tôi đang đặt các giá trị mặc định cho language('en') và một setLanguagehàm sẽ được nhà cung cấp ngữ cảnh gửi đến (các) người tiêu dùng. Đây chỉ là những giá trị mặc định và tôi sẽ cung cấp các giá trị của chúng khi sử dụng thành phần nhà cung cấp trong phần mẹ App.

3. Tạo khách hàng bối cảnh

Để có bộ chuyển đổi ngôn ngữ đặt ngôn ngữ, nó phải có quyền truy cập vào chức năng bộ chuyển đổi ngôn ngữ thông qua ngữ cảnh. Nó có thể trông giống như sau:

class LanguageSwitcher extends Component {
  render() {
    return (
      <LanguageContext.Consumer>
        {({ language, setLanguage }) => (
          <button onClick={() => setLanguage("jp")}>
            Switch Language (Current: {language})
          </button>
        )}
      </LanguageContext.Consumer>
    );
  }
}

Ở đây tôi chỉ đặt ngôn ngữ thành 'jp' nhưng bạn có thể có logic của riêng mình để đặt ngôn ngữ cho điều này.

4. Bao bọc người tiêu dùng trong một nhà cung cấp

Bây giờ tôi sẽ hiển thị thành phần trình chuyển đổi ngôn ngữ của mình trong một LanguageContext.Providervà chuyển các giá trị phải được gửi qua ngữ cảnh đến bất kỳ cấp nào sâu hơn. Đây là cách bố mẹ tôi Apptrông như thế nào:

class App extends Component {
  setLanguage = language => {
    this.setState({ language });
  };

  state = {
    language: "en",
    setLanguage: this.setLanguage
  };

  render() {
    return (
      <LanguageContext.Provider value={this.state}>
        <h2>Current Language: {this.state.language}</h2>
        <p>Click button to change to jp</p>
        <div>
          {/* Can be nested */}
          <LanguageSwitcher />
        </div>
      </LanguageContext.Provider>
    );
  }
}

Bây giờ, bất cứ khi nào trình chuyển đổi ngôn ngữ được nhấp vào, nó sẽ tự động cập nhật ngữ cảnh.

CodeSandbox Demo


mục đích của các giá trị mặc định mà bạn khởi tạo ngữ cảnh là gì? Không phải những mặc định đó luôn bị ghi đè bởi dấu Provider?
ecoe

@ecoe đúng, tuy nhiên trong trường hợp nhà cung cấp không chấp nhận value, người tiêu dùng sẽ sử dụng các giá trị mặc định.
Divyanshu Maithani

1
Tại sao các ngữ cảnh bị giới hạn để đặt / nhận một giá trị đơn giản .... Điều đó có vẻ rất kém hiệu quả. Một ví dụ tốt hơn sẽ là làm nổi bật một ngữ cảnh có mặc định là một đối tượng và cập nhật đối tượng cho phù hợp.
AlxVallejo

Có thể sử dụng các ngữ cảnh cho những gì bạn đang đề cập. Ví dụ trong câu trả lời được trả về dựa trên câu hỏi. Do đó, nó chỉ chứa một giá trị duy nhất cho ngắn gọn.
Divyanshu Maithani

1
Typecript phàn nàn trong trường hợp của tôi nếu setLanguage không có tham số. setLanguage: (language: string) => {}làm việc cho tôi.
alex351

49

Câu trả lời trên của Maithani là một giải pháp tuyệt vời nhưng đó là một thành phần lớp mà không cần sử dụng hook. Vì React khuyến nghị sử dụng các thành phần chức năng và hook nên tôi sẽ triển khai nó với các hook useContext và useState. Đây là cách bạn có thể cập nhật ngữ cảnh từ bên trong một thành phần con.

LanguageContextMangement.js

import React, { useState } from 'react'

export const LanguageContext = React.createContext({
  language: "en",
  setLanguage: () => {}
})

export const LanguageContextProvider = (props) => {

  const setLanguage = (language) => {
    setState({...state, language: language})
  }

  const initState = {
    language: "en",
    setLanguage: setLanguage
  } 

  const [state, setState] = useState(initState)

  return (
    <LanguageContext.Provider value={state}>
      {props.children}
    </LanguageContext.Provider>
  )
}

App.js

import React, { useContext } from 'react'
import { LanguageContextProvider, LanguageContext } from './LanguageContextManagement'

function App() {

  const state = useContext(LanguageContext)

  return (
    <LanguageContextProvider>
      <button onClick={() => state.setLanguage('pk')}>
        Current Language is: {state.language}
      </button>
    </LanguageContextProvider>
  )
}

export default App

1
Tôi đang làm điều này và chức năng thiết lập của tôi bên trong thành phần con tôi luôn là người chúng tôi ban đầu tuyên bố khi tạo bối cảnh:() => {}
Alejandro Corredor

5
Để làm rõ, tôi nghĩ rằng trong ví dụ của bạn state.setLanguage('pk')sẽ không làm bất cứ điều gì, vì const state = useContext(LanguageContext)bên ngoài LanguageContextProvider. Tôi đã giải quyết vấn đề của mình bằng cách di chuyển nhà cung cấp lên một cấp và sau đó sử dụng useContextở cấp độ trẻ em bên dưới.
Alejandro Corredor

2
Trong trường hợp nếu bạn không muốn di chuyển bối cảnh cung cấp một mức độ của bạn lên cũng có thể sử dụng người tiêu dùng bối cảnh như thế này: <LanguageContext.Consumer> {value => /* access your value here */} </LanguageContext.Consumer>.
Mateen Kiani

3
Tôi thực sự thích cách bạn tổ chức tệp LanguageContextMangement.js. Theo quan điểm của tôi, đó là một cách làm rõ ràng và tôi sẽ bắt đầu làm điều đó ngay bây giờ. Cảm ơn bạn!
lustig

2
Cảm ơn vì sự đánh giá cao nó thực sự khuyến khích tôi tiếp tục làm công việc như thế này!
Mateen Kiani

2

Một giải pháp khá đơn giản là đặt trạng thái trên ngữ cảnh của bạn bằng cách bao gồm một phương thức setState trong trình cung cấp của bạn như sau:

return ( 
            <Context.Provider value={{
              state: this.state,
              updateLanguage: (returnVal) => {
                this.setState({
                  language: returnVal
                })
              }
            }}> 
              {this.props.children} 
            </Context.Provider>
        )

Và trong người tiêu dùng của bạn, hãy gọi updateLanguage như sau:

// button that sets language config
<Context.Consumer>
{(context) => 
  <button onClick={context.updateLanguage({language})}> 
    Set to {language} // if you have a dynamic val for language
  </button>
<Context.Consumer>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.