Conversation
Activity block is now external component; Small changes in logycs and design; Added comment to filters.
EvgenyOrekhov
left a comment
There was a problem hiding this comment.
React.Children.only expected to receive a single React element child.
▼ 26 stack frames were expanded.
invariant
node_modules/react/cjs/react.development.js:105
onlyChild [as only]
node_modules/react/cjs/react.development.js:1298
Router.render
node_modules/react-router/es/Router.js:118
finishClassComponent
node_modules/react-dom/cjs/react-dom.development.js:15128
updateClassComponent
node_modules/react-dom/cjs/react-dom.development.js:15083
beginWork
node_modules/react-dom/cjs/react-dom.development.js:15967
performUnitOfWork
node_modules/react-dom/cjs/react-dom.development.js:19093
workLoop
node_modules/react-dom/cjs/react-dom.development.js:19134
HTMLUnknownElement.callCallback
node_modules/react-dom/cjs/react-dom.development.js:147
invokeGuardedCallbackDev
node_modules/react-dom/cjs/react-dom.development.js:196
invokeGuardedCallback
node_modules/react-dom/cjs/react-dom.development.js:250
replayUnitOfWork
node_modules/react-dom/cjs/react-dom.development.js:18341
renderRoot
node_modules/react-dom/cjs/react-dom.development.js:19252
performWorkOnRoot
node_modules/react-dom/cjs/react-dom.development.js:20156
performWork
node_modules/react-dom/cjs/react-dom.development.js:20066
performSyncWork
node_modules/react-dom/cjs/react-dom.development.js:20040
requestWork
node_modules/react-dom/cjs/react-dom.development.js:19895
scheduleWork
node_modules/react-dom/cjs/react-dom.development.js:19702
scheduleRootUpdate
node_modules/react-dom/cjs/react-dom.development.js:20406
updateContainerAtExpirationTime
node_modules/react-dom/cjs/react-dom.development.js:20432
updateContainer
node_modules/react-dom/cjs/react-dom.development.js:20500
ReactRoot.push../node_modules/react-dom/cjs/react-dom.development.js.ReactRoot.render
node_modules/react-dom/cjs/react-dom.development.js:20813
(anonymous function)
node_modules/react-dom/cjs/react-dom.development.js:20967
unbatchedUpdates
node_modules/react-dom/cjs/react-dom.development.js:20283
legacyRenderSubtreeIntoContainer
node_modules/react-dom/cjs/react-dom.development.js:20963
render
node_modules/react-dom/cjs/react-dom.development.js:21030
▲ 26 stack frames were expanded.
Module../src/index.js
src/index.js:5
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
> 5 | ReactDOM.render(<App />, document.getElementById('root'));
View compiled
__webpack_require__
/home/orehov/Downloads/frontend-test-task/webpack/bootstrap:782
779 | };
780 |
781 | // Execute the module function
> 782 | modules[moduleId].call(module.exports, module, module.exports, hotCreateRequire(moduleId));
| ^ 783 |
784 | // Flag the module as loaded
785 | module.l = true;
View compiled
fn
/home/orehov/Downloads/frontend-test-task/webpack/bootstrap:150
147 | );
148 | hotCurrentParents = [];
149 | }
> 150 | return __webpack_require__(request);
| ^ 151 | };
152 | var ObjectFactory = function ObjectFactory(name) {
153 | return {
View compiled
0
http://localhost:3000/static/js/main.chunk.js:977:18
__webpack_require__
/home/orehov/Downloads/frontend-test-task/webpack/bootstrap:782
779 | };
780 |
781 | // Execute the module function
> 782 | modules[moduleId].call(module.exports, module, module.exports, hotCreateRequire(moduleId));
| ^ 783 |
784 | // Flag the module as loaded
785 | module.l = true;
View compiled
checkDeferredModules
/home/orehov/Downloads/frontend-test-task/webpack/bootstrap:45
42 | }
43 | if(fulfilled) {
44 | deferredModules.splice(i--, 1);
> 45 | result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
| ^ 46 | }
47 | }
48 | return result;
View compiled
Array.webpackJsonpCallback [as push]
/home/orehov/Downloads/frontend-test-task/webpack/bootstrap:32
29 | deferredModules.push.apply(deferredModules, executeModules || []);
30 |
31 | // run deferred modules when all chunks ready
> 32 | return checkDeferredModules();
| ^ 33 | };
34 | function checkDeferredModules() {
35 | var result;
View compiled
(anonymous function)
http://localhost:3000/static/js/main.chunk.js:1:57
Исправил. Был неверный import |
EvgenyOrekhov
left a comment
There was a problem hiding this comment.
С точки зрения пользователя есть два замечания по работе приложения:
- поля ввода
- если стереть значения, поля не подсвечиваются красным и не блокируется кнопка "Filter"
- если в первое поле попытаться ввести
a, перейти во второе поле и нажатьb, то в первом поле появитсяa
- модальное окно
- в тексте говорится "You found 5 activities", но фактически пользователь увидел только 4 активности
- мобильный режим
- в интерфейсе "Filters" элементы расположены неэргономично
| <button className='btn btn-small' onClick={modalClose}>Cancel</button> | ||
| </div> | ||
| <div className="col-1-of-2"> | ||
| <Link to="/filter/" className='btn btn-small'>Try!</Link> |
There was a problem hiding this comment.
Можно обойтись без глобальных CSS-классов, воспользовавшись свойством as у styled-components: https://www.styled-components.com/docs/basics#extending-styles (второй пример).
В случае с Button и Link можно сделать так:
const Button = styled.button`
/* ...styles... */
`;
const SmallButton = styled(Button)`
/* ...more styles... */
`;
<SmallButton>Cancel</SmallButton>
<SmallButton as={Link} to="/filter/">Try!</SmallButton>| } | ||
|
|
||
| export default GlobalStyle; | ||
| export { Button, Headline, Container, ContainerInnerWrap, ActivityWrap, Strong, ActivityContent }; No newline at end of file |
There was a problem hiding this comment.
В идеале каждый модуль должен отвечать за и экспортировать что-то одно. Этот модуль можно разделить как минимум на три: GlobalStyle.js, StyledComponents.js, и ActivityContent.js.
src/components/commonData.js
Outdated
| const Price = styled.span` | ||
| font-size: 24px; | ||
| ` | ||
| const ActivityContent = (props) => { |
There was a problem hiding this comment.
Стоит активнее использовать деструктуризацию, особенно в сигнатурах функций. props не говорит ни о чём, а взглянув на const ActivityContent = ({filter, data}) => { сразу становится понятно что в компонент нужно передать.
There was a problem hiding this comment.
data тоже было бы лучше деструктурировать:
const ActivityContent = ({filter, data}) => {
const {
activity,
key,
type
/* ... */
} = data;
return /* ... */;
};Опять же для повышения самодокументированности кода.
src/components/commonData.js
Outdated
| </Price> | ||
| </PriceWrap> | ||
| </div> | ||
| <div className={Object.keys(props.data).length === 1 ? null : 'display-none'}> |
There was a problem hiding this comment.
Повторная проверка условия Object.keys(props.data).length === 1. Возможно было бы лучше воспользоваться вот этой техникой: https://reactjs.org/docs/conditional-rendering.html#inline-if-else-with-conditional-operator.
There was a problem hiding this comment.
Я имел в виду вместо
<div className={isError ? 'display-none' : ''}>
<Success />
</div>
<div className={isError ? '' : 'display-none'}>
<Error />
</div>сделать
{
isError
? <Error />
: <Success />
}There was a problem hiding this comment.
Только теперь div-контейнеры в Success и Error (которые выполняют проверку на data.error) вообще не нужны, так как эта проверка выполняется уровнем выше, в ActivityWrap.
src/components/Activities.js
Outdated
| ` | ||
| function Activity() { | ||
| const [data, setData] = useState({}); | ||
| let [clickCount, setClickCount] = useState(1); |
There was a problem hiding this comment.
Возможно копипаст... Исправил.
| <ContainerInnerWrap> | ||
| <Headline>WhattodoApp</Headline> | ||
| <PreambulaText>{Object.keys(data).length === 0 ? `Don't know what to do? We'll help you! Just click the button and you'll see the possible variant for your vocation...` : `Don't like that? Click... ;-)`}</PreambulaText> | ||
| <Button onClick={getRandomActivity} style={{ marginBottom: '20px' }}>Random</Button> |
There was a problem hiding this comment.
Для стилизации лучше использовать какой-то один способ. Если это styled-components, то все стили должны объявляться с помощью styled-components.
There was a problem hiding this comment.
ОК, учту. Но как быть в том случае, если в одном месте одному элементу нужно задать отступ? Делать новый компонент по аналогии с этим?
| let url = 'http://www.boredapi.com/api/activity'; | ||
|
|
||
| if (filterType === 'priceRange') { | ||
| url = `${url}?minprice=${firstValue}&maxprice=${secondValue}`; |
There was a problem hiding this comment.
Лучше использовать готовый инструмент для работы с query string, например https://www.npmjs.com/package/qs.
|
Теперь если в первое поле попытаться ввести |
Если честно, с этим не знаю, что делать с этим... |
Может быть в данном случае бОльшую часть валидации вообще стоит переложить на плечи браузера? <input type="number" min="1" required> |

Anton Romankov