Создание состояния, доступного во всем приложении, с помощью useContext в React

#javascript #reactjs

Вопрос:

Я пытался использовать useContext и просмотрел несколько видео YT, но я не думаю, что понимаю это…ну, ясно, что это не так. ха

Я хочу сделать this.state.dropdown доступным все приложение, чтобы я мог сделать некоторую условную логику относительно его значения.

 // input.js
import React from 'react';
import axios from 'axios';

import Button from '../components/button';
import { getData } from '../utils/debugger';
import { DropdownContext } from '../utils/DropdownContext';

// Make a separate function for axios call so that you can reuse it
function queryData(dropdownValue) {
  return axios({
    method: 'GET',
    url: `http://xxx/debugger/${dropdownValue}`,
  })
    .then((res) => res.data.map((k) => k['key']['name']))
    .catch((err) => console.error(err));
}

class InputForm extends React.Component {
  // Just use the state property. No need to use constructor
  state = {
    isLoaded: false,
    dropdown: 'RTS',
    value: '',
    data: [],
  };

  // If you use arrow function then you don't have to
  // bind them in the constructor

  handleChange = (event) => {
    this.setState({ value: event.target.value });
  };

  handleDropdown = async (event) => {
    // Here we need to query the data from axios
    // If we get the data then we'll update the state
    // otherwise you may show any error message
    const value = event.target.value;

    try {
      const newData = await queryData(value);
      this.setState({ data: newData, dropdown: value });

      if (newData.length > 0) {
        this.setState({ value: newData[0] });
      }
      console.log('newData = '   JSON.stringify(newData));
    } catch (ex) {
      console.error('Could not get data from axios');
    }
  };

  handleSubmit = (event) => {
    event.preventDefault();
  };

  componentDidMount() {
    queryData(this.state.dropdown)
      .then((data) => {
        this.setState({ data: data });
        if (data amp;amp; data.length > 0) {
          this.setState({ value: data[0] });
        }
      })
      .catch(() => {
        this.setState({ isLoaded: false });
        // Do other stuff here, may be show any error message?
        // Could not get data
      });
  }

  render() {
    return (
        <form onSubmit={this.handleSubmit} className='flex items-center'>
         <DropdownContext.Provide value={this.state.dropdown}>
          <select
            value={this.state.dropdown}
            onChange={this.handleDropdown}
            className='relative w-full bg-white border border-gray-300 rounded-md shadow-sm px-1 py-3 text-center cursor-default focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm mr-5'>
            <option value='RTS'>RTS</option>
            <option value='RTB'>RTB</option>
            <option value='MPC'>MPC</option>
            <option value='MPC_DSP'>MPC_DSP</option>
          </select>
         </DropdownContext.Provide>

          <select
            value={this.state.value}
            onChange={this.handleChange}
            className='relative w-full bg-white border border-gray-300 rounded-md shadow-sm px-1 py-3 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm mr-5'>
            {this.state.data.map((r) => (
              <option key={r} value={r}>
                {r}
              </option>
            ))}
          </select>

          {console.log('---')}
          {console.log('these will be entered into the getData()')}
          {console.log(`this.state.dropdown = ${this.state.dropdown}`)}
          {console.log(`this.state.value = ${this.state.value}`)}

          <Button onClick={() => getData(this.state.dropdown, this.state.value)} color='green'>
            Generate
          </Button>
        </form>
      
    );
  }
}

export default InputForm;
 
 // App.js
import React, { useState, useContext } from 'react';

import './index.css';

import Button from '../src/components/button';

import RTSButtons from '../src/components/rtsButtons';
import RTBButtons from '../src/components/rtbButtons';
import MPCButtons from '../src/components/mpcButtons';

import { DropdownContext } from '../src/utils/DropdownContext';

const sectionStyles = 'mt-5 border-b pb-5';

export default function App() {
  const [buttonStatus, setButtonStatus] = useState('rts');
  const dropdownState = useContext(DropdownContext);

  const switchButtons = (x) => {
    setButtonStatus(x);
  };

  let buttonName = 'RTS / MPC DSP';
  let buttons;

  if (buttonStatus === 'rts') {
    buttonName = 'RTS / MPC DSP';
    buttons = <RTSButtons />;
  } else if (buttonStatus === 'rtb') {
    buttonName = 'RTB';
    buttons = <RTBButtons />;
  } else if (buttonStatus === 'mpc') {
    buttonName = 'MPC';
    buttons = <MPCButtons />;
  }

  return (
    <div>
      <section id='response' className={`flex justify-between ${sectionStyles}`}>
        <div>
          <Button onClick={() => switchButtons('rts')} color='gray'>
            RTS / MPC DSP
          </Button>
          <Button onClick={() => switchButtons('rtb')} color='gray'>
            RTB
          </Button>
          <Button onClick={() => switchButtons('mpc')} color='gray'>
            MPC
          </Button>
        </div>
        <div>
          <Button color='red'>
            <a href='http://exchange-debugger' target='_blank' rel='noreferrer'>
              create a capture
            </a>
          </Button>
          <Button onClick={() => console.log('Feedback was giving')} color='purple'>
            <a
              href='https://docs.google.com/forms/d/e/1FAIpQLSfzebOfAeXqGLqAp5E1l2fW1nTqSzYRwpqKG56HPXey9GQLcA/viewform'
              target='_blank'
              rel='noreferrer'>
              feedback
            </a>
          </Button>
        </div>
      </section>

      <section>
        <h2 className='font-bold text-2xl capitalize -mb-10 mt-2'>{buttonName}</h2>
        {dropdownState} // <-- just testing the value here
      </section>

      <section>{buttons}</section>
    </div>
  );
}
 
 // DropdownContext.js
import { createContext } from 'react';

export const DropdownContext = createContext(null);
 

Это мои компоненты, но я получаю следующую ошибку:

Ошибка: Недопустимый тип элемента: ожидается строка (для встроенных компонентов) или класс/функция (для составных компонентов), но получено: не определено. Скорее всего, вы забыли экспортировать свой компонент из файла, в котором он определен, или вы, возможно, перепутали импорт по умолчанию и именованный импорт. Проверьте метод рендеринга InputForm .

Я не слишком уверен, где искать дальше, кто-нибудь может помочь, это было бы здорово

Комментарии:

1. useContext является крючком, он не работает в компонентах класса. Вы также должны иметь поставщика контекста выше всех дочерних элементов в дереве визуализации, которые вы хотите иметь доступ к контексту, в соответствии с официальными документами .

2. как я могу сделать что-то подобное, используя класс и функциональный компонент? Или есть способ преобразовать компонент моего функционального класса в функциональный?

Ответ №1:

Как указано в документах React: «Контекст предоставляет способ передачи данных через дерево компонентов без необходимости передавать реквизиты вручную на каждом уровне». Состояние для передачи определено в вашем InputForm классе, поэтому использование контекста позволит вам получить доступ state.dropdown к дереву компонентов InputForm . Похоже, вы хотите получить доступ к состоянию из App класса, но я не вижу, чтобы такой компонент был частью дерева компонентов InputForm . Если это так, то вам нужно будет определить состояние в общем предке компонентов и переместить DropdownContext.Provider его к этому предку.