Форум программистов, компьютерный форум, киберфорум
React/ReactJS
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.68/19: Рейтинг темы: голосов - 19, средняя оценка - 4.68
59 / 49 / 14
Регистрация: 23.02.2016
Сообщений: 433

Callback с аргументами

12.08.2021, 17:08. Показов 3820. Ответов 5
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
В react приложении есть метод render()

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
render(
  return(
    <div className="msu">
      {listData.slice(0, 5).map(i => {
        switch(i) {
          case 0:
            return(this.createMsuMarkup(i));
          case 1: break;
          default: break;
      })}
  );
)
В котором вызывается метод
JavaScript
1
2
3
4
createMsuMarkup = (i) =>
  <div key={i} className="msu__content">
    {this.createCustomSelect(this.selectMsu(0, 13, "layout-2-column"))}
  </div>
В котором вызывается метод с коллбэком
JavaScript
1
2
createCustomSelect = (callback) => 
  callback()
У меня имеется набор методов, например, такой с аргументами
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
selectMsu(start, stop, parentName) {
  return function() {
    inserts.slice(start, stop).map(i => (
      <CustomSelect
        title = {msuTitles[i]}
        index = {this.props.index}
        modes = {this.props.modes}
        parent = {parentName}
        id = {i}
        value = {this.props.msu.get(msuKeys[i])}
        onSelect = {this.props.onSelectMSU}
      />
    ))
  }
}
И я хотел бы их вызывать через createCustomSelect(...) как-то так например
JavaScript
1
{this.createCustomSelect(this.selectMsu(0, 13, "layout-2-column"))}
Но браузер ругается
Code
1
2
3
4
5
6
7
8
9
10
11
TypeError: this is undefined
selectMsu/</<
 
 
  20 | inserts.slice(start, stop).map(i => (
  21 |   <CustomSelect
  22 |     title = {msuTitles[i]}
> 23 |     index = {this.props.index}
     | ^  24 |     modes = {this.props.modes}
  25 |     parent = {parentName}
  26 |     id = {i}
В чем может быть проблема?

Добавлено через 35 минут
Та же самая ошибка и с таким вызовом
JavaScript
1
{this.selectMsu(0, 13, "layout-2-column")()}
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
12.08.2021, 17:08
Ответы с готовыми решениями:

Использование метода из dll (C++) с callback аргументами в C# программе?
Уже всю голову сломал, не могу разобраться с тем, как сопоставить типы данных параметров. Расскажу подробнее. Есть радиоприемник...

For_each и аргументы callback-функции; Как передать callback'у больше одного аргумента
Изучаю контейнеры и алгоритмы stl по Майерсу . С непривычки слегка охренел и запутался в них . В общем есть у меня простой вызов...

Работа с аргументами
Задание такое: Написать программу с использованием функции, которая сравнивает две строки, заданные в качестве её аргументов. Если аргумент...

5
Эксперт JS
 Аватар для Iverycool
1586 / 796 / 362
Регистрация: 01.02.2019
Сообщений: 1,047
13.08.2021, 05:50
Лучший ответ Сообщение было отмечено Timurs как решение

Решение

Timurs, у вас классическая ошибка с this.
У функции selectMsu контекст - ваш класс компонента, а у возвращаемой функции this будет undefined, так как она не является методом, т.е. не имеет своего "родительского" объекта - не имеет this. (В нестрогом режиме, кстати, "родительский" объект был бы - globalThis - window для браузера).

Решений в вашем случае несколько:
1. Непонятно зачем вы используете тут фабрику функций и "колбэки", это выглядит напрасным усложнением, да и, вообще, фабрика функций в таком виде не является распространённой практикой в React. Так что первое решение - упростить код.
2. Чтобы получить доступ к "родительскому" this можно воспользоваться стрелочными функциями, которые никогда не имеют собственного this:
JavaScript
1
2
3
4
5
6
7
selectMsu(start, stop, parentName) {
  return () => {
    inserts.slice(start, stop).map(i => (
      <CustomSelect
        title = {msuTitles[i]}
        index = {this.props.index}
        ...
3. Старый вариант (до es6) - сохранение родительского this в переменной, часто называемой self:
JavaScript
1
2
3
4
5
6
7
8
selectMsu(start, stop, parentName) {
  return function() {
    inserts.slice(start, stop).map(i => (
      <CustomSelect
        title = {msuTitles[i]}
        index = {self.props.index}
        modes = {self.props.modes}
        ...
4. В данном случае не очень подходящий вариант, но стоит знать о нём - bind:
JavaScript
1
2
3
4
5
6
7
8
9
10
selectMsu(start, stop, parentName) {
  var self = this; // var использован намеренно)
  return function() {
    inserts.slice(start, stop).map(i => (
      <CustomSelect
        title = {msuTitles[i]}
        index = {this.props.index}
        ...
  }.bind(this);
}
В данном случае я советую вам воспользоваться первым вариантом, в работе с this вообще - вторым. В случаях с классами часто вместо bind можно использовать методы классов, объявленные как свойства классов.

Ну, и почитать:
https://learn.javascript.ru/ob... -v-metodah
https://learn.javascript.ru/bind

Не по теме:

А почему бы вам не использовать функциональные компоненты? По функциональности они сейчас практически равны классам, а новичкам, ИМХО, освоить их легче: там нет this? (Хотя хуки...)

0
59 / 49 / 14
Регистрация: 23.02.2016
Сообщений: 433
13.08.2021, 10:40  [ТС]
Iverycool, спасибо, через анонимные функции не получилось, а через стрелочные вчера всё-таки сделал, спасибо, интересно!

Вчера пришла мысль, что коллбэк позволит мне повторно использовать код, не понимал вначале как именно, тем более и написать не получалось, но общая идея такая - у меня имеется множество блоков, у всех этих блоков из общей разметки лишь враппер, тайтл и контент, но контент везде разный, в одном блоке селекты в две колонки, в другом вначале просто селект по середине, а под ним две колонки или три, а под ними ещё там как-то, в третьем вообще другие компоненты либо разные компоненты, причем зачастую в блоке компонентов много, то есть нужен цикл, пришла идея вынести общую часть в метод, на разметку контента конкретного блока писать конкретный для этого блока метод (получается да, сделать фабрику функций для фабрики блоков) и прокидывать коллбэком в общий метод. Получилось вроде бы ниче так

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  templateMarkup = (create) =>
    create()
 
  createMarkup = (i, markup, title, content) =>
    <div key={i} className={markup[0]}>
      <h3 className={markup[1]}>{title}</h3>
      <div className={markup[2]}>
        {content()}
      </div>
    </div>
 
  msuContentMarkup = (firstBlockComponents, secondBlockComponents) =>
    <div className="layout-2-column">
      <div className="MSU-GS1__content">
        {firstBlockComponents()}
      </div>
      <div className="MSU-GS2__content">
        {secondBlockComponents()}
      </div>
    </div>
 
  selectMsu = (start, stop, parentName) =>
    inserts.slice(start, stop).map(i => (
      <CustomSelect
        key  = {i}
        title = {msuTitles[i]}
        index = {this.props.index}
        modes = {this.props.modes}
        parent = {parentName}
        id = {i}
        value = {this.props.msu.get(msuKeys[i])}
        onSelect = {this.props.onSelectMSU}
      />
    ))
И вызов такой
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div className="additional-content">
  {listData.slice(0, 5).map(i => {
    switch(this.props.index) {
      case 0:                   
        switch(i) {
          case 0:
            return(this.templateMarkup(
              () => this.createMarkup(i, msuMarkup, "МСУ-ГС",
                () => this.msuContentMarkup(
                  () => this.createCustomSelect(0, 13, "layout-2-column"),
                  () => this.createCustomSelect(13, 26, "layout-2-column"),
                ))
              )
            );
          case 1: 
          case 2: break;
          default: break;
        }
      break;
      case 1: break;
    }
  })}
</div>
Нагромождения свич-кейсов не избежать, внешний свич это вкладка, внутренний это элементы-блоки на это вкладке.
В дальнейшем планирую сделать компонент Tab и вынести внутрянку в него.
На самом деле я перепилил экзэмпл https://github.com/aholachek/react-flip-toolkit stagger effects с хуков на классы и использовал его как остов для своего приложения, мне с классами как-то психологически проще) Но не только поэтому, сервер на котором будет размещаться приложение не шибко новый и если честно я не знаю какая версия ноды и реакта там вообще работать будет...

Добавлено через 18 минут
selectMsu забыл переименовать на createCustomSelect, то есть этот метод не привязан к блоку.
0
Эксперт JS
 Аватар для Iverycool
1586 / 796 / 362
Регистрация: 01.02.2019
Сообщений: 1,047
13.08.2021, 13:05
Лучший ответ Сообщение было отмечено Timurs как решение

Решение

Цитата Сообщение от Timurs Посмотреть сообщение
через анонимные функции не получилось, а через стрелочные вчера всё-таки сделал
Стрелочные функции тоже могут быть анонимными)

А вам самим не кажется, что ваш код слишком нагромождён? У вас всего чуть более 50 строчек в общем, но они ни капли не интуитивно понятны) В вашем случае надо применять композицию компонентов (код без jsx даже получится чем-то похожим ):
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Названия для компонентов стоит поискать получше
 
function Markup({ key, title, markup, children }) {
  // массив markup лучше деструктурировать и дать элементам осмысленные имена
 
  return (
    <div key={i} className={markup[0]}>
      <h3 className={markup[1]}>{title}</h3>
      <div className={markup[2]}>
        {children}
      </div>
    </div>
  );
}
 
function ContentMarkup({ left, right }) {
  return (
    <div className="layout-2-column">
      <div className="MSU-GS1__content">
        {left}
      </div>
      <div className="MSU-GS2__content">
        {right}
      </div>
    </div>
  );
}
 
function SelectMsu({ start, stop, parentName }) {
  // свойства из this.props тоже надо сюда передать через пропсы или через контекст
  return inserts.slice(start, stop).map(i => (
    <CustomSelect
      key={i}
      title={msuTitles[i]}
      index={this.props.index}
      modes={this.props.modes}
      parent={parentName}
      id={i}
      value={this.props.msu.get(msuKeys[i])}
      onSelect={this.props.onSelectMSU}
    />
  ));
}
И непосредственно рендер будет такой:
JavaScript
1
2
3
4
5
6
<Markup key={i} markup={msuMarkup}, title="МСУ-ГС">
  <ContentMarkup
    left={<SelectMSU start={0} stop={13} parentName="layout-2-column" />}
    right={<SelectMSU start={13} stop={26} parentName="layout-2-column" />}
  />
</Markup>
И, конечно, нужно вынести функционал табов в отдельный компонент. В рендере вообще должно быть минимум логики.
Цитата Сообщение от Timurs Посмотреть сообщение
Но не только поэтому, сервер на котором будет размещаться приложение не шибко новый и если честно я не знаю какая версия ноды и реакта там вообще работать будет...
Подскажу: даже самый последний (17.0.2) react согласно своему package.json поддерживает ноду ">=0.10.0". Так что не отговорка
Для компонентов с состоянием - согласен применять классы ещё допустимо, но для stateless-компонентов (часто UI) - функции однозначно проще.
Если вы поймёте хуки, то вряд ли останетесь на классах)

И почитайте официальные доки, они и на русском есть:
https://ru.reactjs.org/docs/hooks-intro.html
https://ru.reactjs.org/docs/co... tance.html
https://ru.reactjs.org/docs/context.html
https://ru.reactjs.org/docs/hi... nents.html
https://ru.reactjs.org/docs/render-props.html
https://ru.reactjs.org/docs/thinking-in-react.html
1
59 / 49 / 14
Регистрация: 23.02.2016
Сообщений: 433
13.08.2021, 15:04  [ТС]
Iverycool, красиво у Вас получилось, спасибо, надо переделать также, но я столкнулся с проблемой, кажется, Вы упомянули о ней уже

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function SelectMsu({ start, stop, parentName }) {
  // свойства из this.props тоже надо сюда передать через пропсы или через контекст
  return inserts.slice(start, stop).map(i => (
    <CustomSelect
      key={i}
      title={msuTitles[i]}
      index={this.props.index}
      modes={this.props.modes}
      parent={parentName}
      id={i}
      value={this.props.msu.get(msuKeys[i])}
      onSelect={this.props.onSelectMSU}
    />
  ));
}
Вот мне бы этот момент переиспользовать как-то, перепробовал кучу способов, но не получается.
Тут явно указаны опции this.props.modes, по сути это this.props.modesMSU, ну и эти пропсы явные this.props.msu.get(msuKeys[i]), this.props.onSelectMSU, msuTitles[i], то есть они относятся к блоку MSU.
У меня имеется ещё блок BSSD, у него уже this.props.modesBSSD, this.props.bssd.get(bssdKeys[i]), this.props.onSelectBSSD, bssdTitles[i].

Правда с фигурными скобками не пробовал, получается что аргумент объект, может и прокатит
JavaScript
1
function SelectMsu({ start, stop, parentName }) {// компонент в цикле }
Добавлено через 1 час 7 минут
Iverycool, спасибо, не знал, что так можно, всё работает!
0
59 / 49 / 14
Регистрация: 23.02.2016
Сообщений: 433
19.08.2021, 13:32  [ТС]
Iverycool, а не подскажите как такие вещи в отдельный класс выносить
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
function ContentMarkup({ left, right }) {
  return (
    <div className="layout-2-column">
      <div className="MSU-GS1__content">
        {left}
      </div>
      <div className="MSU-GS2__content">
        {right}
      </div>
    </div>
  );
}
Я написал вот так
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class ContentMarkup extends Component {
 
    state = {
        left: this.props.children,
        right: this.props.children
    }
 
 
    render() {
        return(
            <div className="layout-2-column">
 
                <div className="MSU-GS1__content">
                    {this.state.left}
                </div>
 
                <div className="MSU-GS2__content">
                    {this.state.right}
                </div>
 
          </div>
        );
    }
}
 
export default ContentMarkup;
Но тогда этот код не работает
JavaScript
1
2
3
4
5
6
import ContentMarkup from '../content/ContentMarkup';
 
<ContentMarkup
    left={<SelectMSU start={0} stop={13} parentName="layout-2-column" />}
    right={<SelectMSU start={13} stop={26} parentName="layout-2-column" />}
  />
Добавлено через 1 час 28 минут
Разобрался, всё проще даже оказалось

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ContentMarkup extends Component {
 
    render() {
        return(
            <div className="layout-2-column">
 
                <div className="MSU-GS1__content">
                    {this.props.left}
                </div>
 
                <div className="MSU-GS2__content">
                    {this.props.right}
                </div>
 
          </div>
        );
    }
}
 
export default ContentMarkup;
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
19.08.2021, 13:32
Помогаю со студенческими работами здесь

Функция с аргументами
Написать функцию с аргументами вещественный массив x размер массива n. Функция возвращает значение суммы элементов массива, начиная с...

макросы с аргументами
подскажите как правильно написать макрос с аргументом хочу сделать чтото типо того #define bPos(x)...

Работа с аргументами
Программа получает в качестве аргумента имя файла и последовательность символов. Эти символы записываются в обратном порядке в файл.

Работа с аргументами
Программа получает в качестве аргумента имя файла и последовательность символов. Эти символы записываются в обратном порядке в файл.

Работа с аргументами ф-й в С++
Задача: Написать прог. с ф-ей, что сравнивает две строки, заданных в качестве её аргументов (знач. 2-го рядка задать &quot;по...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
6
Ответ Создать тему
Новые блоги и статьи
Символьное дифференцирование
igorrr37 13.02.2026
/ * Логарифм записывается как: (x-2)log(x^2+2) - означает логарифм (x^2+2) по основанию (x-2). Унарный минус обозначается как ! в-строка - входное арифметическое выражение в инфиксной(обычной). . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL3_image
8Observer8 10.02.2026
Содержание блога Библиотека SDL3_image содержит инструменты для расширенной работы с изображениями. Пошагово создадим проект для загрузки изображения формата PNG с альфа-каналом (с прозрачным. . .
Установка Qt-версии Lazarus IDE в Debian Trixie Xfce
volvo 10.02.2026
В общем, достали меня глюки IDE Лазаруса, собранной с использованием набора виджетов Gtk2 (конкретно: если набирать текст в редакторе и вызвать подсказку через Ctrl+Space, то после закрытия окошка. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru