2016-10-10 3 views
0

Я пытаюсь изучить React. Я уже хорошо разбираюсь в javascript. Я пытаюсь учиться, создавая небольшое приложение, которое в основном является диспетчером задач. В моем случае это касается предметов, связанных с продуктом. У меня есть скрипка, созданная здесь. Не могли бы вы взглянуть на то, как я составил код для реагирования и дайте мне знать, если это лучший подход к построению с компонентами/классами? Вы можете видеть, что у меня есть вложенные компоненты. Я не уверен, есть ли лучший способ сделать это.Learning React.js

И, наконец, я не создавал новую строку «add-item-row» каждый раз, когда пользователь нажимает на синюю кнопку «Добавить». В настоящий момент один отображается по умолчанию, но по умолчанию я не хочу показывать его. Я хочу создать созданную (add-item-row, div) только тогда, когда пользователь нажимает кнопку «Добавить».

Вот скрипка.

https://jsfiddle.net/j0mpsbh9/4/

<div id="app" class="container"> 
     <script type="text/babel"> 
     var AddItemWrapper = React.createClass({ 
    render: function() { 
     return (
      <div> 
       <div className="row"> 
        <AppTitle /> 
        <AddItemForm /> 
       </div> 
        <AddItemRow /> 
      </div> 
     ); 
    } 
}); 

var AppTitle = React.createClass({ 
    render: function() { 
     return (
      <div> 
       <h1>Grocery List</h1> 
      </div> 
     ); 
    } 
}); 

var AddItemForm =React.createClass({ 
    render: function() { 
     return (
      <div> 
       <div className="col-sm-6 col-lg-6"> 
        <div className="form-group"> 
        <label htmlFor="enter-grocery-item" className="sr-only">Enter Grocery Item</label> 
        <input type="text" className="form-control" id="enter-grocery-item" placeholder="Enter Grocery Item" /> 
        </div> 
       </div> 
       <div className="col-sm-6 col-lg-6"> 
        <button type="button" id="add" className="btn btn-block btn-info">Add <span className="glyphicons circle_plus"></span></button> 
       </div> 
      </div> 
     ); 
    } 
}); 

var AddItemRow =React.createClass({ 
    render: function() { 
     return (
      <div className="add-item-row"> 
       <div className="row"> 
        <div className="col-sm-12 grocery-items"> 
         <div className="col-sm-6"> 
          <div className="form-group"> 
          <label htmlFor="grocery-item" className="sr-only">Grocery Item</label> 
          <input type="text" className="form-control" id="grocery-item" placeholder="" /> 
          </div> 
         </div> 
         <div className="col-sm-6 center"> 
          <button type="button" className="btn btn-blockx btn-warning"><span className="glyphicons pencil"></span></button> 
          <button type="button" className="btn btn-blockx btn-lgx btn-danger"><span className="glyphicons remove"></span></button> 
          <button type="button" className="btn btn-blockx btn-lgx btn-success"><span className="glyphicons thumbs_up"></span></button> 
         </div> 
        </div> 
       </div> 
      </div> 
     ); 
    } 
}); 

ReactDOM.render(
    <AddItemWrapper />, 
    document.getElementById('app') 
); 
    </script> 
</div> 
+0

Вы должны использовать модули (например, webpack) для разделения компонентов/контейнеров на разные файлы. – jbpark

ответ

1

У вас есть хороший старт здесь! Ваша иерархия компонентов организована разумным образом. Однако вам не хватает какой-либо интерактивности или внутреннего состояния.

Основной способ, с помощью которого интерактивные компоненты React взаимодействуют, заключается в использовании state плюс обратные вызовы событий, которые модифицируют упомянутый state. «Состояние» довольно самоочевидно - оно описывает значения, присущие тому, как компоненты выглядят и ведут себя, но которые меняются со временем. Каждый раз, когда компонент React state изменяется (с this.setState()), этот компонент будет повторно рендерить (в буквальном смысле, повторно запустив функцию render()), чтобы отразить изменения.

Сначала давайте отредактируем класс AddItemWrapper, чтобы отслеживать некоторое внутреннее состояние, когда оно впервые установлено. Мы знаем, что вы хотите иметь несколько строк данных, поэтому давайте дадим ему пустой массив для хранения будущей информации о строках:

getInitialState(){ 
    return {rows: []}; 
}, 

Теперь вместо рендеринга одного AddItemRow напрямую, мы окажем динамический набор строки, основанные на текущем состоянии компонента. Array.map() идеально подходит для этого и общий случай использования в React:

{this.state.rows.map(function(ea, i){ 
    return <AddItemRow initialItemName={ea} key={ea + "-" + i} /> 
})} 

В основном то, что делает это принять каждую запись в массиве AddItemWrapper.state.rows массива и делает его в качестве компонента AddItemRow. Мы даем ему два свойства: initialItemName и key. «initialItemName» просто сообщит дочернему компоненту, как его имя было, когда оно было впервые добавлено, а «ключ» - это уникальная строка, которая позволяет React различать компоненты от своих братьев и сестер.

Теперь мы настроили AddItemWrapper для правильной рендеринга строк на основе его внутреннего состояния. Затем мы должны изменить AddItemForm, чтобы он реагировал на ввод пользователя и запускал новые добавляемые строки.

В AddItemForm сначала нам нужно добавить «ref» в текстовое поле ввода.Это связано с тем, что React может идентифицировать и считывать данные из этого HTML-элемента после того, как он визуализируется:

<input ref={function(el){this.inputElement = el;}.bind(this)} ... /> 

Затем дать кнопочного элемента обратного вызова, который будет запускать, когда он нажал:

<button onClick={this.handleClick} ... /> 

Наконец написать Обратный вызов обработчика:

handleClick(){ 
    this.props.onAdd(this.inputElement.value); 
    this.inputElement.value = ""; 
} 

Обратите внимание, как этот обратный вызов звонит this.props.onAdd()? Это означает, что нам нужно передать функцию обратного вызова из родительского (AddItemWrapper) в этот компонент для использования. Так мы общаемся между родителями и детьми в React: передайте функцию от родителя к ребенку, которая будет вызвана изнутри дочернего элемента, но будет влиять на родителя.

В AddItemWrapper мы уверены AddItemForm имеет доступ к функции обратного вызова:

AddItemForm onAdd={this.onAdd} /> 

И тогда мы пишем саму функцию обратного вызова:

onAdd(newItem){ 
    var newRows = this.state.rows.slice(); 
    newRows.push(newItem); 
    this.setState({rows: newRows}); 
} 

Обратите внимание, как мы копируя старый массив провел в state (с использованием Array.slice()), введите новый элемент в новый массив и затем обновите состояние с помощью нового массива? Никогда не мутируйте state напрямую; ВСЕГДА скопируйте его, измените копию и затем обновите state с новой копией.

Практически сделано. Мы создали способ для AddItemWrapper для рендеринга его строк и способ для AddItemForm для создания новых строк. Теперь мы редактируем AddItemRow для рендеринга таким образом, чтобы поддерживать собственное внутреннее состояние.

Сначала убедитесь, что он инициализирует свое состояние, когда он установлен. Мы будем следить за строковым значением, которое изначально совпадает с тем, что пользователь вводил в текстовое поле при нажатии «Добавить», но поскольку он хранится в AddItemRow.state, он может быть впоследствии изменен пользователем:

getInitialState() { 
    return {itemName: this.props.initialItemName} 
} 

Теперь, когда имя строка хранится в состоянии компонента, мы можем сделать его в HTML, как это:

<input value={this.state.itemName} ... /> 

Here's what it looks like when you put it all together!

Есть, очевидно, больше функций, которые вы хотели бы добавить , например, позволяя пользователю редактировать, перемещать или d elete запись строки. Я оставлю эти упражнения до вас. Я настоятельно рекомендую вам прочитать все the official documentation, а также сделать несколько руководств, чтобы получить голову в игре. Очевидно, что у вас есть небольшой опыт под вашим поясом, учитывая то, что у вас было до сих пор, но получить вид, как state, render(), а работа обратных вызовов требует некоторой практики. Удачи!

+0

Большое вам спасибо за помощь! – user3259232