← Назад в блог

Как я связал React-компоненты с внешним JS: история разработчика

Опубликовано
2 мин чтения
--- просмотров

В рамках текущей рабочей задачи у меня был кейс — вызвать модалку, созданную как React-компонент, из SharePoint Ribbon. Короче говоря, SharePoint ribbon — это структура, которая содержит кнопки и привязанные к ним js-функции. Когда ты нажимаешь кнопку, вызывается привязанная к ней js-функция... надеюсь, ты понимаешь :). И я начал гуглить: как взаимодействовать с react-модулем из внешнего js, как изменить react-стейт из внешнего js, как форсировать ре-рендер react-компонента из чистого js и так далее... Так вот, основная проблема в том, что нельзя привязать функцию react-компонента к кнопке ribbon, потому что она спрятана глубоко внутри webpack js-бандла. Также мне нужно было менять стейт React-модуля, потому что я хотел ре-рендерить компонент по клику на кнопку. Поэтому я сделал fiddle, и, похоже, всё работает корректно — https://jsfiddle.net/o6ujb5g2/. Это не SharePoint ribbon, но почти то же самое. Идея в том, чтобы прикрепить функцию из react-модалки к глобальному объекту window. Этот объект существует практически везде.

Шаги:

  1. Я создал функцию-заглушку на чистом js для window.func, которую хочу переопределить из компонента (это для подстраховки, если с рендером компонента что-то пойдёт не так)
  2. В компоненте я инициализирую функцию с бизнес-логикой, которая меняет стейт компонента
  3. В componentDidMount я переопределяю глобальную window.func на module.func
  4. ПРОФИТ!!!

Листинги исходного кода:

HTML
<script>
window.toggleModal = function(isOpened) {
  console.log(`[x] Loading... Passed value: ${isOpened}`);
}
</script>


<input type="button" onclick="js:window.toggleModal(true);" value="click me"/>
<div id="app" var1="test"></div>
React-модуль
class TodoApp extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      isModalOpened: false,
      items: [
        { text: Learn JavaScript, done: false },
        { text: Learn React, done: false },
        { text: Play around in JSFiddle, done: true },
        { text: Build something awesome, done: true }
      ]
    }
  }

  componentDidMount = () => {
    window.toggleModal = this.toggleModal;
  }

  toggleModal = (isOpened) => {
    this.setState({
      ...this.state, isModalOpened: isOpened
    });
  }

  render() {
    return (
      <div>
        <h2>Todos:</h2>
        <ol>
        {this.state.items.map(item => (
          <li key={item.id}>
            <label>
              <input type=checkbox disabled readOnly checked={item.done} />
              <span className={item.done ? done : }>{item.text}</span>
            </label>
          </li>
        ))}
        </ol>
        {this.state.isModalOpened && (
        <React.Fragment>
          <p>modal opened!</p>
          <input type=button onClick={() => this.toggleModal(false)} value=close />
        </React.Fragment>)}
      </div>
    )
  }
}

ReactDOM.render(<TodoApp />, document.querySelector(#app))

Открыт для работы по контракту

Я доступен для работы по контракту. Если у вас есть интересная идея проекта — запишитесь на звонок через Calendly.

Записаться на 30-минутный звонок