const monthToInt = val => {
val = val.toLowerCase();
switch (true) {
case !!val.match(/^янв/): return 0;
// TODO добавить больше кейсов
}
}
const dateFromLocaleString = date =>
new Date(
...date
.split(" ")
.reduce(
(acc, val) =>
(acc = [val.match(/\D/) ? monthToInt(val) : parseInt(val), ...acc]),
[]
)
)
dateFromLocaleString('07 янв 2019')
class TodoList extends React.Component {
state = {
todos: [
'Commit',
'Push'
]
}
render() {
return <ul>
{this.state.todos.map(item => {
return <li>{ todo }</li>
}
</ul>
}
}
const todos = [
'Init',
'Commit',
'Push'
]
// Начальный стейт
<ul>
<li>Commit</li>
<li>Push</li>
</ul>
// Добавлен элемент
<ul>
<li>Init</li> // <- разница начинается здесь и до конца древа
<li>Commit</li>
<li>Push</li>
</ul>
const todos = [
'Commit',
'Push',
'Merge'
]
// Начальный стейт
<ul>
<li>Commit</li>
<li>Push</li>
</ul>
// Добавлен элемент
<ul>
<li>Commit</li>
<li>Push</li>
<li>Merge</li> <- разница начинается здесь, от начала и до сих по ничего не менялось
</ul>
<li>Commit</li>
и <li>Push</li>
не менялись, однако реакт недостаточно умён чтобы это понять. Чтобы помочь ему следует воспользоваться специальным пропом key={}
. Он может быть значением любого типа, единственно требование — значение должно стабильно идентифицировать соответствующие данные.class TodoList extends React.Component {
state = {
todos: [
{ id: 0, text: 'Commit' },
{ id: 1, text: 'Push' }
]
}
render() {
return <ul>
{this.state.todos.map(item => {
return <li key={todo.id}>{ todo.text }</li>
}
</ul>
}
}
const todos = [
{ id: 2, text: 'Init' },
{ id: 0, text: 'Commit' },
{ id: 1, text: 'Push' }
]
// Начальный стейт
<ul>
<li>Commit</li> // id 0
<li>Push</li> // id 1
</ul>
// Добавлен элемент
<ul>
<li>Init</li> // id 2 новый элемент отобразится в начале
<li>Commit</li> // id 0
<li>Push</li> // id 1
</ul>
Math.random()
в качестве ключа, так вы почти гарантировано будете всегда получать нестабильные идентификаторы.const { name, email } = obj
CategoiesFilter
расширит класс React.Component
, вам будет нужно реализовать метод render
. Тогда вы сможете избавиться от селекторов по заранее написанному html.<button class="slowly" data-target="some_id">Take me there but slowly</button>
$(".slowly").on("click", function (event) {
/*Отменяем стандартную обработку нажатия по ссылке.*/
event.preventDefault();
/*Забираем идентификатор блока с атрибута href.*/
var id = $(this).attr('href') || $(this).attr('data-target');
/*Узнаём высоту от начала страницы до блока, на который ссылается якорь.*/
var top = $(id).offset().top;
/*Анимируем переход на расстояние - top за 1000ms.*/
$('body,html').animate({scrollTop: top}, 1200);
<template>
<form>
<p v-if='errorMessage'>{{ errorMessage }}</p>
<input type='text' v-model='username'/>
<input type='password' v-model='password'/>
<button @click='send()'>Log in</button>
</form>
</template>
<script>
import someHttpClient from 'someHttpClient'
export default {
data: {
return {
username: '',
password: '',
errorMessage: ''
}
},
methods: {
send () {
var username, password, errorMessage = this
if (username && password) {
errorMessage = ''
someHttpClient.sendAndMaybeCipher({ username, password })
} else {
errorMessage = 'Form not filled properly'
}
}
}
}
</script>
var date = new Date()
if (date.getDate() === 25 && date.getMonth() === 11) {
showMessage()
}
File
это специальный типBlob
~ MDN
File
в качестве аргумента метода #send
XMLHttpRequest
, чтобы отправить его в теле POST запроса. Например:function eventHandler (event) {
const file = event.files[0];
const xhr = new XMLHttpRequest();
xhr.open('POST', '/someroute', true)
xhr.onreadystatechange = function () {
if (xhr.status === 200) {
console.log(xhr.reponseText);
}
}
}
xhr.send(file);
{
xtype: 'triggerfield',
listeners: {
change: function(newVal, oldVal, field) {
console.log('new value', newVal);
console.log('old value', oldVal);
console.log('field', field);
}
}
}
const data = {whatever: 'payload'};
// Создадим объект xhr для обработки запроса
const xhr = new XMLHttpRequest();
// Открываем запрос
// @param String - типзапроса
// @param String - путь до слушателя
// @param Boolean - true async / false sync
// Запрос ещё не отправлен
xhr.open('POST', '/someroute', true)
// Указываем что это AJAX запрос
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
// Указываем что это JSON payload
xhr.setRequestHeader('Content-Type', 'application/json');
// Коллбек запускается при смене состояния xhr
xhr.onreadystatechange = function () {
if (xhr.status === 200) {
console.log(xhr.reponseText);
}
};
// Отправляем запрос
xhr.send(JSON.stringify(data));
const data = {whatever: 'payload'};
// Начнём с заголовков
const headers = new Headers();
headers.append('Content-Type', 'application/json');
// Функция должна вернуть объект Promise
return fetch('/someroute', {
method: 'POST',
headers: headers,
body: JSON.stringify(data);
}).then(raw => raw.json()).then(response => {
console.log(response);
});
export default {
// mounted неплохой вариант, если document должен быть ready
mounted () {
const $ = window.$
$('.main').onepage_scroll()
}
}
export default {
directives: {
// Произвольное имя директивы
onePageScroll: {
// На этом хуке компонент со всеми дочерними компонентами готов
componentUpdated (el) {
const $ = window.$
// Елемент не нужно «селектить» по классу/ИД т.к. он передан в аргументы директивы
$(el).onepage_scroll()
}
}
}
}
<div>
<section v-one-page-scroll />
</div>