Реакция компонентов без состояния: взаимодействие с их выводом и внешним видом - PullRequest
1 голос
/ 13 января 2020

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

Ниже приведен очень простой пример того, что я имею в виду (и, надеюсь, лучше проиллюстрирует то, что я спрашиваю):

Предположим, у нас есть несколько книжных документов, таких как

bookList = [
{
  title: "book 1", 
  author: "bob", 
  isbn: 1, 
  chapters: [
{ chapterNum: 1, chapterTitle: "intro", chapterDesc: "very first chapter", startPg: 2, endPg: 23 }, 
{ chapterNum: 2, chapterTitle: "getting started", chapterDesc: "the basics", startPg: 24, endPg: 45 }
]},
{
 title: "book 2" ... }
]

Таким образом, главное, что эти встроенные объекты в документах могут быть очень длинными и, следовательно, могут быть свернуты / развернуты.

И вот примерный пример кода, показывающего компоненты

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            books: bookList,
            focusBook: null
        }
        this.updateDetailDiv = this.updateDetailDiv.bind(this);
    }

    updateDetailDiv(book) {
        this.setState(
            { focusBook: book}
        );
    }

    render() {
        return(
            <BookList 
                bookList = {this.state.books} 
                updateDetailDiv = { this.updateDetailDiv }
            />
            <BookDetail
                focusBook = { this.state.focusBook }
            />
        );
    }
}

const BookList = props => {
    return (
        props.bookList.map(item=>
            <li onClick={()=> props.updateDetailDiv(item)}> {item.title} </li>
            )
        );
}

const BookDetail = props => {
     return (
        <div className="bookDetails">
        { props.focusBook != null
        ? <div>
            {props.focusBook.title}, 
            {props.focusBook.author},
            {props.focusBook.isbn}
            Chapters:
            <div className="chapterList">
                { props.focusBook.chapters.map(item=>
                    <span onClick={()=>someFunction(item)}>{item.chapterNum} - {item.chapterName}</span>
                    )}
            </div>
            <div id="chapterDetails">
                This text will be replaced with the last clicked chapter's expanded details
            </div>
        </div>
        : <div>
            Select A Book
         </div> 
     })
}

someFunction(item) {
 document.getElementById('chapterDetails').innerHTML = `<p>${item.chapterDesc}</p><p>${item.startPg}</p><p>${item.endPg}</p>`;
}

Так что моя проблема в том, что я не уверен, каков наилучший подход к работе с простой косметикой c / визуальные изменения данных в функциональных компонентах без сохранения состояния без передачи их родительскому компоненту - что хорошо и имеет смысл для первого ребенка - но что происходит, когда у многих детей будут свои собственные дети (у которых могут быть свои собственные дети) -> все требуют свои варианты рендеринга? Например, здесь компонент App будет повторно отображать компонент DetailDiv (поскольку состояние изменилось), но я не хочу, чтобы приложение также обрабатывало подробный div DetailDiv. В моем примере все очень просто, но само приложение, над которым я работаю, имеет 2 или 3 слоя встроенных элементов, которые - однажды отрендеренные App - могут реально визуально измениться обычным JS.

Так что в моем примере вы увидите, что у меня есть someFunction() в каждом списке глав - я могу сделать эту работу, написав отдельную простую «традиционную JS DOM-функцию» (ie: target.getElementById или closest() - но я не думаю, что я должен использовать обычный JS для манипулирования DOM при использовании React.

Итак, еще раз, чтобы подвести итог - каков наилучший способ обработки простое манипулирование DOM для рендеринга вывода компонентов без состояния? Превращение их в их собственный класс кажется излишним - и «родительское» приложение обрабатывает своих «внуков», и состояние «правнуков» будет громоздким по мере роста приложения. Должно быть, пропущен очевидный пример, потому что я не видел много способов справиться с этим без слоев с состоянием компонентов.

EDIT для ясности:

BookDetail - это компонент без сохранения состояния.

Родительский компонент с состоянием (App) * передает объект как опору (*1031*)

. Когда состояние App изменяется, он будет возвращаться снова, отражая изменения.

Предположим, BookDetail отвечает за отображение большого количества данных.

Я хочу, чтобы каждый из span в BookDetail при нажатии, отобразит соответствующий item в chapterDetail div. Если щелкнуть по другому span, то chapterDetail div заполняется данными item. (это всего лишь простой пример - это может быть любое другое чистое изменение внешнего вида для какого-либо компонента без состояния - когда кажется, что родитель должен излишне следить за ним)

Я не знаю, как изменить UI / внешний вид компонента без состояния после его рендеринга, не присваивая ему состояния, ИЛИ не заставляя родителя отслеживать то, что по сути является «подсостоянием» (поскольку единственный способ обновить внешний вид компонента - это изменить его состояние, вызывая рендер).

Есть ли способ сделать это, не делая BookDetail компонентом с состоянием?

Ответы [ 2 ]

1 голос
/ 13 января 2020

Вы можете добавить немного простого состояния к функциональным компонентам для отслеживания выбранного индекса. В этом случае я бы сохранял «выбранный индекс главы» в состоянии и затем отображал в div «chapters [index] .details», все без манипуляций с DOM, который является анти-паттерном React.

здесь используется тот случай, когда выбранная глава является внутренней деталью, о которой only BookDetail заботится, поэтому не поднимайте это «состояние» до родительского компонента, так как это также актуально только в течение жизненного цикла BookDetail, поэтому нет необходимости хранить этот выбранный индекс в системе управления состоянием всего приложения, например, приставки.

const BookDetail = ({ focusBook }) => {
  // use a state hook to store a selected chapter index
  const [selectedChapter, setSelectedChapter] = useState();

  useEffect(() => setSelectedChapter(-1), [focusBook]);

  if (!focusBook) {
    return <div>Select A Book</div>;
  }

  const { author, chapters, isbn, title } = focusBook;

  return (
    <div className="bookDetails">
      <div>
        <div>Title: {title},</div>
        <div>Author: {author},</div>
        <div>ISBN: {isbn}</div>
        Chapters:
        <div className="chapterList">
          {chapters.map(({chapterName, chapterNum}, index) => (
            <button
              key={chapterName}
              onClick={() => setSelectedChapter(selectedChapter >= 0 ? -1 : index)} // set the selected index
            >
              {chapterNum} - {chapterName}
            </button>
          ))}
        </div>

        // if a valid index is selected then render details div with 
        // chapter details by index
        {chapters[selectedChapter] && (
          <div id="chapterDetails">
            {chapters[selectedChapter].details}
          </div>
        )}
      </div>
    </div>
  );
};

DEMO

Edit adoring-chandrasekhar-yt3y1

1 голос
/ 13 января 2020

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

Теперь, если вы не хотите использовать React Hooks, вы можете использовать React Redux store для управления всеми вашими состояниями: вы можете изменять значения состояний только с помощью действий Redux.

Счастливого кодирования! : D

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...