Основные принципы и советы по JS
- Не считаю что чем больше кода, тем лучше. Более короткий код, на мой взгляд, легче читается и выглядит более изящным. Если что то можно написать короче, при условии что оно работает таким же образом, то я делаю это.
- Ничего плохого в том чтобы писать кучу условий на проверку тех или параметров, но если условия никогда не будут работать это плохо и это лишний код, который загрязняет проект.
Пример:
checkImg(user) {
if (!user) return;
...
var src = user && user.profileImageURL;
...
return(
...)
}
Взглянув на этот код можно заметить, что условие user && — никогда не сработает правильно, потому что условие выше выбросит из функции раньше. Обычно такие костыли возникают, когда человек делает вставки или торопиться (как это делал я).
- Оператор && очень удобен для проверки переменной user на наличие объекта, а затем обращении к свойству объекта. Нельзя обращаться к свойствам объекта, если он равен undefined.
- Избегать чрезмерной вложенности условий. Для этого можно использовать такое правило в коде. Я бы назвал это правило метод выброса.
Пример:
мainCalc(obj){
if(!obj) return; // условие выхода из функции
...
... какой-то код
...
if(...) return; // след. условие выхода
...
...
if (...) return; // каждый след. часть кода зависит от предыдущих частей
...
...
return; // таким образом производится допуск к коду без вложенности
}
— Простой пример уменьшения кода, где я написал функцию проверки и вывода картинки. В случае если значение свойства равнялось null или пустой строке, то вывести картинку по умолчанию.
BAD
checkImg(user) {
if (user){
if (user.profileImageURL !== undefined){
if (user.profileImageURL === null) {
return (<DefultImage />)
}
else{
if (user.profileImageURL.length>0){
return (<img src={user.profileImageURL} className="avatar photo" alt="" />)
}else{
return (<DefultImage />);
}
}
}
else {
return;
}
}
return;
}
GOOD
checkImg(user) {
var u = user&&user.profileImageURL;
if (u===undefined) return;
if (!u) return (<DefultImage />);
return (<img src={u} className="avatar photo" alt=""/>);
},
Важно отметить, что необходимость проверки зависит от разных условий. Если из примера выше мы знаем, что в user у нас всегда попадает значение типа объект, то проверять его нет смысла, так как в JS даже пустой объект всегда true. Поэтому будет правильней обратиться сразу к свойству объекта, которое там должно быть.
if (user.profileImageURL !== undefined){...
— Условия проверки можно писать более коротким способом:
if (user.profileImageURL){...
Это работает одинаково, а наша задача уменьшить код как это возможно.
— Но часто бывают ситуации, когда у нас вложенность выше, чем просто объект –> свойство, допустим, есть вложенный объект и обращаться к его свойствам напрямую уже нельзя, поэтому нужна проверка вложенного объекта
— Установка библиотек это отдельная тема потому что одни библиотеки экономят кучу времени другие могут дать вам обратный эффект прежде чем вы найдете подходящую и полностью настроите ее под ваши нужды, но если можно обойтись тремя строчками кода, то я думаю это плохая затея ставить отдельную либу для этого.
Переходим к react-redux
1. Для начинающих делать проект на JS с использованием react-redux я советую не начинать проект с настройки WebPack, Babel, linter. Ответ на вопрос почему, находится в этой статье.
Ден Абрамов – создатель redux — также советует использовать Create React App.
2. Структура построения архитектуры приложения это тоже отдельная тема и поэтому я решил изучить этот вопрос подробнее после исследования десятков статей мне попалась статья разработчика. Который советует строить структуру не так как это сделано в большинстве примеров. Для больших проектов это огромный плюс, если следовать этим правилам.
Вот структура компонентов моего проекта и в каждом разделе свои actions и reducers:

И поэтому при высокой вложенности папок не приходится писать вот такие вещи:

Также не приходится видеть в папке actions 100500 файлов. Для небольшого проекта это конечно может быть не очень важно, но для большого мне кажется очень удобно. Вы конечно все actions можете хранить в одном файле, но это на мой взгляд тоже не очень правильно, так как в каждый компонент импортируются функции actions всех компонентов.
3. Вот ссылка на мой написанный проект — там не все идеально, но в нем можно посмотреть структуру приложения. Но так уж сложилось, что в этом проекте я больше не участвую. Пусть этот код не лежит без дела.
4. При помощи PropTypes мы можем определить, какие параметры должен принимать компонент и в случае передачи в компонент неверных параметров будет выведена ошибка или предупреждение, а также можем установить значения по умолчанию. Но без этой штуки можно вполне обойтись и не добавлять их в проект по одной простой причине вы можете проверить данные до передачи их в компонент:
{this.state.visibleID&&(<ViewIcons location={this.state.location} visibleID={this.state.visibleID} />)}
Важное дополнение, компонент ViewIcons будет рендерится только после того как в переменную visibleID попадет какое либо значение, плюс если в доп. компоненте идет обращение к API то это поможет нам получить эти параметры в процедуре componentDidMount() в которой и следует делать все запросы. В свою очередь значения по умолчанию для компонента можно сделать через state. Ну если вам кажется что стоит использовать PropTypes, то используйте, потому что цель этой статьи уменьшать код а не увеличивать.
5. Так как в redux store у меня получает данные с сервера, то до момента пока данные не придут он равен пустому объекту, но допустим с сервера придет не объект, а массив объектов и обратится к какому либо свойству уже нельзя, для этого можно проверить первый элемент массива что он не равен undefined.
{this.props.Menu[0]&&this.createMenu()}
6. Локальный state компонента можно использовать для того чтобы сначала получить значения из store, но это мне кажется не всегда оправданно, тут как грань двух концов:
- в первом случае придется делать переменные локального state и присваивать им значения затем проверять их в функции render;
- во втором случае если не использовать локальный state, мы будем обращаться к свойствам объектов или элементу массива которые находятся в store;
- и третий, лучший на мой взгляд, это присваивать по умолчанию для элементов redux store пустую строку или ноль и тогда не будет необходимости обращаться к свойствам объектов или первым элементам массива (undefined назначить элементу store нельзя).
const menu = (state='', action) => { switch(action.type) { case "MENU_TREE":
Это просто моменты, с которыми я столкнулся на практике. Если кто-то знает как сделать изящней, то непременно пишите, буду рад.
Комментарии (22)
DexterHD
27.02.2018 19:372. Структура построения архитектуры приложения это тоже отдельная тема и поэтому я решил изучить этот вопрос подробнее после исследования десятков статей мне попалась статья разработчика. Который советует строить структуру не так как это сделано в большинстве примеров. Для больших проектов это огромный плюс, если следовать этим правилам.
Вот уже за это я ставлю +. Респект.
serf
27.02.2018 20:48Чем этот код хороший?
checkImg(user) { var u = user&&user.profileImageURL; if (!u) return; if (u === null || u.length === 0) return (<DefultImage />); return (<img src={u} className="avatar photo" alt=""/>); },
Проверка на пустую строку 3 раза, причем не самым лучшим образом (почему например просто !u вместо u.length === 0 не использовать)?
Вот вариант более краткий:
checkImg({profileImageURL}) { return profileImageURL ? <img src={profileImageURL} className="avatar photo" alt=""/> : <DefultImage />; },
Вот структура компонентов моего проекта и в каждом разделе свои actions и reducers:
Такой подход к структурирования файлов многими используется, называется grouping by feature. Помимо прочего это может быть полезно например при lazy загрузках, по очевидным причинам.
И поэтому при высокой вложенности папок не приходится писать вот такие вещи:
Вместо путей вида .../.../.../../some/module/etc можно использовать алиасы.okhn
01.03.2018 13:01checkImg({profileImageURL}) { return profileImageURL ? <img src={profileImageURL} className="avatar photo" alt=""/> : <DefultImage />; },
а если на вход в функцию checkImg отправят user который undefined, то у вас код упадет на ошибке Cannot read property 'profileImageURL' of undefined.
я бы сделал вот так вот
const checkImg = (user) => { return user && user.profileImageURL ? <img src={user.profileImageURL} className="avatar photo" alt=""/> : <DefultImage />; };
rovkin1 Автор
01.03.2018 13:09посмотрите код внимательней:
checkImg(user) { var u = user&&user.profileImageURL; if (u===undefined) return; if (!u) return (<DefultImage />); return (<img src={u} className="avatar photo" alt=""/>); },
если user равен undefined в переменную u ничего не запишется и она тоже будет undefined и след условие нас выбросит из функции. никакой ошибки не будетapapacy
01.03.2018 14:26Ваша функция возвращает в этом случае undefined а для построения дерева компонентов нужен или компонент или null.
rovkin1 Автор
01.03.2018 14:57ошибок в консоли у меня с этим не было. Но возможно присваивать нулл это правильно не задумывался над этим.
apapacy
01.03.2018 18:42не было наверное потому что условие не срабатывало. попробуйте вернуть прямо сразу undefined. Задумываться об этом не имеет смысло. Просто React так работает. Если бы в React было предусмотрено в этом случае возвращать undefined или пустую строку то так бы и слеодвало поступать. Но Нужен компонент или nullю Или в последних версиях добавился еще массив из этих элементов. Что наконец избавило разработчиков от необзодимости все заворачивать в бесконечное количество div для каждого компонента не имеющего одного родительского элемента.
rovkin1 Автор
01.03.2018 23:45Вы говорите на самом деле немного о других вещах. Возвращение null или компонента необходимо только для самих компонентов реакта. Это функция не является компонентом она использовалась в цикле где в параметр передавался текущий объект! Скажу вам более возвращение null нулл в данной ситуации привело бы к ошибке.
apapacy
02.03.2018 13:00Как используется эта функция по вашему коду определить не получается. Я предположил что если в ряде случаев функция возвращает компонент то это и есть компонент. Если что то это кажется логичным чтобы функция имела возвращаемое значение одного типа вне зависимости от га входных данных.
serf
01.03.2018 18:59или так
checkImg({profileImageURL} = {}) { return profileImageURL ? <img src={profileImageURL} className="avatar photo" alt=""/> : <DefultImage />; },
rovkin1 Автор
28.02.2018 00:18Код хороший в плане короче. его я уже поправил так как нужно. На счет алиасов может быть да, не использовал их еще.
apapacy
28.02.2018 04:44checkImg(user) { var u = user&&user.profileImageURL; if (u===undefined) return; if (!u) return (<DefultImage />); return (<img src={u} className="avatar photo" alt=""/>); },
При некоторых условиях функция вернет вместо компонента undefined. Это будет ошибка при построении дерева компонентов. Нужно возвращать null. см. reactjs.org/docs/conditional-rendering.html
Поэтому будет правильней обратиться сразу к свойству объекта, которое там должно быть.
Где-то я читал что до сих пор обращение к несуществующему объекту первая или вторая по распространенности «ошибка на миллион баксов». Она в таком коде конечно не исключается.
Можно порекомендовать использовать линтер для анализа кода. КОд приведенный статье навскидку проверку такую не пройдет. А там много есть чего в линтере хорошего.
gogolor
rovkin1 Автор
по 1 заметил уже поправил. а вот на счет 2 то я в конце писал как этого избежать там три варианта знаете лучше напишите.
gogolor
Чтобы уж совсем попридираться, у вас в BAD код упадет при checkImg({ profileImageURL: 0 }), а в GOOD вернет <DefultImage />
Честно говоря, я даже и половины не понял из того что вы написали.
rovkin1 Автор
Прежде всего это написано для тех кто использует реакт редакс. Мне кажется, что те кто с этим сталкиваются, понять смысл будет не трудно.
gogolor
Нет, мне непонятно, что вы хотели написать. Возьмем тот же пункт 6
Тут я не понял, как state можно использовать для получения значения? Вы мб хотели сказать что-то вроде "Значения, полученные из store, можно записать в локальный state"?
Мб "палка о двух концах"? Почему? В чем плюсы и минусы этого решения? Мб пример приведете?
Какие значения? Полученные из redux? Зачем?
Зачем необходимо обращаться к свойствам объектов или первым элементам массива? Приведите адекватный пример.
rovkin1 Автор
да вы первое правильно поняли это и хотел сказать! Второе палка или грань без разницы.
для удобной проверки значения! Потому что есть необходимость проверить есть ли у нас данные в редакс сторе
gogolor
Так забейте туда null. Как получите значение — забейте туда значение. Если этот, очевидный, вариант не подходит, то приведите пример, где он не подходит.
rovkin1 Автор
У меня была ошибка. Мог конечно ошибиться но если у вас все работает с null (начальным состоянием redux стора), то без проблем используйте с null.
rovkin1 Автор
При нуле и не нужно в этом примере в параметр передавался либо пустой объект либо заполненшый.
gogolor
{ profileImageURL: 0 } — вполне заполненный объект. Вы переписали функцию и изменили ее поведение. Плохой пример.