JSFiddle - React, Tailwind, and code Playground

HTML

<table class="table table-stripped" id="compositions_table">
		<thead>
			<tr>
				<th style="width:40%">Название</th>
				<th style="width:40%">Значение</th>
				<th style="width:20%"></th>
			</tr>
		</thead>
		<tbody>
				<tr class="item">
		<td class="name">
			<input type="text" value="" class="wfull" placeholder="Название"/>
		</td>
		<td class="value">
			<input type="text" value="" class="wfull" placeholder="Значение"/>
		</td>
		<td class="control">
			<button type="button" data-role="remove-line" class="btn btn-danger btn-block">
				<i class="fa fa-times"></i> Удалить
			</button>
		</td>
	</tr>
		</tbody>
		<tfoot>
			<tr>
				<td colspan="3" style="text-align:right">
					<button type="button" class="btn btn-success" data-role="save-compositions" style="margin-left:15px;float:right">
						<i class="fa fa-save"></i> Сохранить
					</button>
					<div id="message-box" style="line-height:30px"></div>
				</td>
			</tr>
		</tfoot>
	</table>

JavaScript

var __compositions_index=-1,
	____compositions_timer;

$(function(){
	var $table=$('#compositions_table');

	$table.find('.item').each(set_listener).end()
		.find('[data-role="save-compositions"]').click(function(){
			var $t=$(this);

			if($t.attr('disabled')!==undefined)
				return;

			$t.attr('disabled',true);
			var props={};
			$('#compositions_table [data-index].item').each(function(){
				var $inputs=$(this).find('input');
				props[$inputs.eq(0).val()]=$inputs.eq(1).val();
			});
			$.post(location.href.replace('mode=edit','mode=save_compositions'),{
				props:props
			},function(res){
				try{
					if(!!____compositions_timer)
						clearTimeout(____compositions_timer);

					if(typeof res=='string')
						res=JSON.parse(res);

					$('#message-box').text(
						!!res.message?res.message:(
							res.result!=='ok'?'Произошла ошибка':'Сохранено'
						)
					);
					____compositions_timer=setTimeout(function(){
						$('#message-box').text('');
					},5000);

				} catch(e){
					console.error(e);
				}

				$t.removeAttr('disabled');
			});
		});
});

function set_listener(index,row){
	var $row=$(row);

	if($row.attr('data-index')!==undefined)
		return;

	$row.attr('data-index',++__compositions_index).find('input').each(function(i){
		this.setAttribute('data-name',(i?'value':'name')+'['+__compositions_index+']');
	}).on('input blur',function(){
		var $row=$(this).parents('[data-index]'),
			$clone;

		if(!$row.is(':last-child'))
			return;

		$row.find('input').filter(function(){
			return this.value.trim().length<=0;
		}).length==0 && (
			$clone=$row.clone().removeAttr('data-index').find('input').val('').removeAttr('data-name').end()
		) &&
			$row.parents('tbody').append($clone).find('tr:last').each(set_listener);
	}).end().find('[data-role="remove-line"]').click(function(){
		var $tr=$(this).parents('tr');
		if($tr.parent().find('tr').length>1)
			$tr.remove();
		else
			$tr.find('input').val('');
	});
}