Ответы пользователя по тегу React
  • Пара вопросов о реализации фильтров в Реакте?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    По-хорошему у вас должен быть запрос на сервер с сортировкой, пагинацией, фильтрацией и лимитом.
    На каждый клик по сортировке, пагинатору или фильтру запрос. Например:
    GET '/api/v1/animals?page=1&limit=20sort=name&sortDirection=DESC&priceFrom=1000&priceTo=2000'

    То что вы хотите реализовать, как минимум, вряд ли найдет применение в реальном приложении.
    Учитесь решать реальные задачи.

    А трюки с display: none, при возможностяx react вообще можно не использовать.

    Даже если пока не можете реализовать сервер с бд и REST API. Правильней будет хранить состояние фильтра в state компонента и выдавать на редер отфильтрованный по параметрам фильтра из state массив с животными. Изменился фильтр - произошел рендер. Вот и вся задача.
    Ответ написан
    1 комментарий
  • Как мне отправить форму авторизации, чтобы выходили данные?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Так:
    class UserForm extends React.Component{
      state = {
        login: '',
        password: '',
      };
      
      handleChange = e => {
        const { name, value } = e.target;
        
        this.setState({
          [name]: value,
        });
      };
      
      handleSubmit = e => {
        const { login, password } = this.state;
    
        e.preventDefault();
        alert(`
          login: ${login}
          password: ${password}
        `);
      };
      
      render(){
        const { login, password } = this.state;
    
        return(
          <div>
            <form onSubmit={this.handleSubmit}>
              <Fields
                text="Введите логин"
                type="text"
                name="login"
                value={login}
                onChange={this.handleChange}
               />
              <Fields
                text="Введите пароль"
                type="password"
                value={password}
                name="password"
                onChange={this.handleChange}
               />
              <MuiThemeProvider>
                <RaiseButtin
                  label="Войти"
                  fullWidth={true}
                  style={style.button}
                 >
                   <input
                     type="submit"
                     style={style.exampleImageInput}
                   />
                 </RaiseButtin>
              </MuiThemeProvider>
            </form>
          </div>
        );
      }
    }


    Обратите внимание, что добавлены свойства(атрибуты) name для Fields.

    Демо: https://jsfiddle.net/tkvz8ggn/
    Ответ написан
  • React как удалить элемент?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Примерно так:
    import React, { Component } from 'react';
    
    class List extends Component {
      state = {
        elements: [
          {
            id: 1,
            title: 'First',
          },
          {
            id: 2,
            title: 'Second',
          },
        ],
      };
      
      handleDeleteElement = id => {
        this.setState(prevState => ({
          elements: prevState.elements.filter(el => el.id != id);
        }));
      };
      
      render() {
        const { elements } = this.state;
        
        return (
          <ul>
            {elements.map(el => (
              <li
                key={el.id}
                onClick={() => { this.handleDeleteElement(el.id) }}
              >
                {el.title}
              </li>
            ))}
          </ul>
        )
      }
    }
    Ответ написан
    Комментировать
  • Почему не устанавливается свойство key, хотя оно существует?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Правильно это делать так:
    {items.map(item => (
      <Item key={item.id} content={item} />
    )}

    Свойство key надо определять снаружи компонента элемента списка, а не внутри.

    Вот ссылка на документацию, где есть примеры правильного и неправильного использования с пояснениями
    https://reactjs.org/docs/lists-and-keys.html#extra...
    Ответ написан
    Комментировать
  • Как передать функцию react js?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Так:

    transitionToAnchor(id, event){
        this.searchDataPopup(id);
        this.props.onClose();
        event.preventDefault();
    }
    Ответ написан
    Комментировать
  • Как избавиться от проблемы которая вызвана преобразование кода webpack код react?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Переделайте так:
    module.exports = {
      context: __dirname + "/public/js/",
      entry: "./AuthForm",
      output: {
        filename: "bundle.js",
        path: path.resolve(__dirname + "public/src")
      },
      module: {
        rules: [
          {
            test: /\.js$/,
            exclude: /node_modules/,
            use: [
              {
                loader: "babel-loader",
                options: {
                  presets: ['es2015', 'stage-0', 'react']
                }
              }]
          }
        ]
      }
    };

    не забудьте установить пресеты для babel:
    npm install -S babel-preset-es2015 babel-preset-react babel-preset-stage-0


    То, что у вас исходники в папке public, как минимум, семантически неверно, переименуйте в src, client или app
    Ответ написан
    Комментировать
  • Использование Api сайтом, нужно ли?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Почему нельзя, просто, выделить common часть для обоих модулей?
    А в модель поста можно прописать метод который будет доставать и мапить результаты.

    Тогда у вас будет примерно такой код в rest:
    router.get('/', routeCache.cacheSeconds(20), async (req, res) => {
        const posts = await models.Post.getPostsList({ limit: 10 });
    
        res.json(posts);
    });


    и такой для сайта:
    router.get('/', async (req, res) => {
        const posts = await models.Post.getPostsList({ limit: 10 });
    
        res.render('index', { posts_list: posts });
    });


    Все просто и никаких костылей.
    Обращения сервера сайта к rest это лишний код и лишние операции.

    Метод res.json() сам добавляет к ответу заголовок Content-Type со значением 'application/json' если он не добавлен до этого.
    Ответ написан
    Комментировать
  • Почему реакт достает токен из куков лишь однажды?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Ваши пояснения больше путают, чем помогают. Вопрос не понял, но думаю дело в том, что токен достаете при инициализации. А надо дергать при каждом вызове makeParams:
    const makeParams = params => ({
      headers: {
        Authorization: `Bearer ${cookies.get('frl_jwt_token')}`,
      },
      params: { ...params },
    })
    Ответ написан
    Комментировать
  • Как правильно получать обновленный Store в React Reduxe?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    В render такие действия выполнять нельзя. Хоть dispatch и выполняется синхронно, свойства получаемые компонентом иммутабельны, а это значит, что компонент получит новые свойства, выполнит все функции жизненного цикла и получит новый массив men, только в следующий render.

    Для добавления вашей сущности можно использовать хандлер:
    class ActionsReducers extends React.Component {
      handleAddMan = () => {
        this.props.addMan({ name: 'Альберт', family: 'Эйнштейн' });
      };
      
      render() {
        const { men } = this.props;
    
        return (
          <Wrapper>
            <List>
              {men.map(man => <Man key={man.id} man={man} />)}
              {!men.length && <Empty>No data in list.</Empty>}
            </List>
            <Button onClick={this.handleAddMan}>Add man</Button>
          </Wrapper>
        );
      }
    }


    Или один из методов жизненного цикла:
    class ActionsReducers extends React.Component {
      componentDidMount() {
        this.props.addMan({ name: 'Альберт', family: 'Эйнштейн' });
      }
    
      render() {
        const { men } = this.props;
    
        return (
          <Wrapper>
            <List>
              {men.map(man => <Man key={man.id} man={man} />)}
              {!men.length && <Empty>No data in list.</Empty>}
            </List>
          </Wrapper>
        );
      }
    }
    Ответ написан
  • Как в React Reduxe получить Store переданный через Provider?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Макс Мачез, вам не надо обращаться к store напрямую в компоненте, если вы используете react-redux.
    Вы не должны мапить весь стор, как это делаете в примере в комментарии, мапьте только данные необходимые компоненту:
    const mapStateToProps = state => ({
      userData: state.userData,
    });


    по-хорошему, для получения части объекта состояния, надо использовать selector pattern, отличная его реализация reselect:
    import { createSelector } from 'reselect';
    
    export const userDataSeleror = state => state.userData;
    
    export const userSelector = state => createSelector(
      userDataSelector,
      userData => userData.user,
    );
    
    export const isUserDataLoadingSelector = state => createSelector(
      userDataSelector,
      userData => userData.isLoading,
    );
    
    export const isUserDataErrorSelector = state => createSelector(
      userDataSelector,
      userData => userData.isError,
    );


    тогда контейнер для компонента будет примерно такой:
    import { fetchUserData } from './somePlace';
    import Profile from './Profile';
    import {
      userSelector,
      isUserDataLoadingSelector,
      isUserDataErrorSelector,
    } from './selectors';
    
    const mapStateToProps = state => ({
      user: userSelector(state),
      isLoading: isUserDataLoadingSelector(state),
      isError:  isUserDataErrorSelector(state),
    });
    
    const mapDispatchToProps = {
      fetchUserData,
    };
    
    export default connect(mapStateToProps, mapDispatchToProps)(Profile);


    Компонент внутри вообще ничего не должен знать про redux, он лишь получает набор обязательных свойств и использует, а откуда они приходят ему не важно:
    class Profile extends Component {
      componentDidMount() {
        this.props.fetchUserData();
      }
      
      render() {
        const { user, isLoading, isError } = this.props;
        return (
           ...
        );
      }
    }


    Все, что вы передаете посредством декоратора connect в компонент, доступно в последнем в его свойствах (this.props), аргументы поэтому так и называются mapStateToProps и mapDispatchToProps. Все actionCreators переданные объектом в mapDispatchToProps оборачиваются в dispatch, внутренним вызовом bindActionCreators.
    Ответ написан
    2 комментария
  • Как задать отображение spinner до ответа от сервера reactJS?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Идеальный вариант, создать для этих задач компонент:
    function Block({ 
      children,
      isLoading, 
      isError, 
      isEmpty, 
      emptyText, 
      fetch,
    }) {
      const showPreloader = isLoading && !isError;
      const allDataIsReady = !isLoading && !isError;
      const showEmptyText = allDataIsReady && isEmpty && emptyText;
    
      return (
        <Wrapper>
          {showPreloader && <Preloader />}
          {isError && <TryAgain fetch={fetch} />}
          {showEmptyText && <Empty>{emptyText}</Empty>}
          {allDataIsReady && children}
        </Wrapper>
      );
    }


    И использовать его в коде так:
    render() {
      const { isLoading, isError, orderList } = this.props;
    
      return (
        <Block
          isLoading={isLoading}
          isError={isError}
          isEmpty={!ordersList.length}
          fetch={this.fetchOrdersList}
          emptyText="You don't have any orders."
        >
          {ordersList.map(order => <Order key={order.id} order={order} />}
        </Block>
      )
    }


    Название служебных дочерних компонентов компонента Block условные, но думаю суть их задач передают.
    Ответ написан
    4 комментария
  • Как улучшить Route, react?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Роуты:
    export const routes = [
      {
        component: Home,
        path: '/',
      },
      {
        component: Roster,
        path: '/roster',
     },
     {
        component: Schedule,
        path: '/schedule',
     },
    ];

    Компонент:
    export default function SwitchWithRoutes({ routes }) {
      return (
        <Switch>
          {routes.map((route, i) => (
             <Route
               key={i}
               exact={route.path === '/'}
               path={route.path}
               component={route.component}
             />
           )}
        </Switch>
      );
    }


    Так вы сможете при необходимости написать функцию getRoutes с нужным вам поведением или фильтровать роуты в зависимости от прав, законнектив SwitchRoutes на стор и добавив в роуты дополнительные ключи.
    Ответ написан
    6 комментариев
  • Как сделать отображение элемента при перезагрузке страницы?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Я вам писал в прошлом вопросе как это сделать.
    Измените свой роут:
    <Route path="/more/:id" component={More}/>

    измените ссылку:
    <Link to={`more/${this.props.itemId}`} className="descr">подробнее</Link>


    измените компонент:
    export default function More(props){
        const { id } = props.match.params;
        const item =props.items[id];
        const { id, title, descr, img, price } = item;
        
        return(
            <div className="more__info">
               ...
            </div>
        );
    }


    А ссылка для перехода будет не такая:
    localhost:3000/more_id1
    а, при id = 1 такая:
    localhost:3000/more/1
    Ответ написан
    7 комментариев
  • Как передать массив с данными от одного компонента к другому?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    По-хорошему redux.
    Но вы так же можете передавать список из App в Items с колбек функцией, которая запишет в state компонента App id выбранного продукта, ее надо прокинуть через Items в каждый Item и там по клику вызывать:
    class App extends Component {
      state = {
        activeItemId: -1,
      };
      
      selectItem = id => {
        this.setState({
           activeItemId: id,
        });
      };
    
      render() {
        const { items } = this.props;
        const { activeItemId } = this.state;
       
        const activeItem = items.find(item => item.id === selectedItemId);
    
        return (
          <Wrapper>
            <More item={activeItem} />
            <Items 
              items={items}
              onSelectItemCalback={this.selectItem}
            />
          </Wrapper>
        );
      }
    }
    Ответ написан
    Комментировать
  • Как обратиться к конкретному объекту массива?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Если надо построить список ссылкок, то массив должен быть примерно такого вида:
    const links = [
      {
        label: 'Home',
        path: '/',
      },
      {
        label: 'About',
        path: '/about',
      },
      {
        label: 'Shop',
        path: '/shop',
      },
      {
        label: 'Home',
        path: '/',
      },
      {
        label: 'Contact',
        path: '/contact',
      },
    ];

    Строится так:
    return (
      <Wrapper>
        {links.map((link, i) => (
           <Link key={i} to={link.path}>
             {link.label}
           </Link>
         ))}
      </Wrapper>
    );


    Если же вам просто надо обратиться к конкретному элементу:
    this.state.href[0] // первый элемент массива
    Ответ написан
    Комментировать
  • Как должен выглядеть тест для action, react/redux?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Исправленный вариант:
    it('test openModal', () => {
        expect(actions.openModal({ name: 'Some name'}, '1')).toEqual({
          type: types.OPEN_MODAL,
          payload: {
            id: '1',
              initData: {
                name: 'Some name',
            },
          },
        });
      });


    Вы вместо аргументов передавали в actionCreator объект action, который actionCreator должен на основе аргументов создавать и возвращать. В итоге он у вас возвращал:
    {
      type: types.OPEN_MODAL,  // тут естественно значение
      payload: {
        id: undefined,
        initData: {
          id: '1',
          initData: {
            name: 'Some name',
          },
        },
      },
    }
    Ответ написан
    1 комментарий
  • Правильно ли так отслеживать viewport?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Можно использовать applyContainerQuery из react-container-query.
    Это будет лучше, чем по всему приложению ставить и снимать слушатели в функциях жизненного цикла.
    import { applyContainerQuery } from 'react-container-query';
    import { Hello, Boy, Wrapper } from '../somePlace';
    
    const myQuery = {
      hasMinWidth: {
        minWidth: 1024,
      },
    };
    
    class MyComponent extends Component {
      ...
      render() {
        const {
          containerQuery: { hasMinWidth },
        } = this.props;
    
        return (
          <Wrapper>
            {hasMinWidth ? <Hello /> : <Boy />}
          </Wrapper>
        );
      }
    }
    
    export default applyContainerQuery(MyComponent, myQuery);
    Ответ написан
    2 комментария
  • Почему react/redux не рендерит компонент?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Вы мутируете старое состояние, естетсвенно у вас ничего не работает.
    Исправить можно так:
    export default function(state = initialState, action) {
       if (action.type === 'DIALOG_SELECTED') {
          return state;
       }
       if (action.type === 'DIALOG_DELETE') {
          const newState = [...state];
          newState.shift();
    
          return newState;
       }
       else {
          return state;
       }
    }


    bindActionCreators с connect не используют. Бессмысленно, так как connect сам его вызывает. Так будет лаконичней:
    const mapStateToProps = state => ({
      dialogs: state.dialogs,
    });
    
    const mapDispatchToProps = {
      selectDialog: actions.selectDialog,
      deleteDialog: actions.deleteDialog, 
    };
    
    export default connect(mapStateToProps, mapDispatchToProps)(Sidebar);


    Функции биндить каждый рендер не лучшая идея. Используйте class field function:
    class Sidebar extends Component {
    
       selectDialog = () => {
          this.props.selectDialog();
    
       };
       deleteDialog = () => {
          this.props.deleteDialog();
       };
    
       render() {
          console.log('рендер сайдбара');
          return (
             <div className='sidebar'>
                <div className='sidebar__content'>
                   <div className='sidebar__head'></div>
                   <SidebarChatGroup chatName='Общий'>
                      <button onClick={this.selectDialog}>Выбрать</button>
                      <button onClick={this.deleteDialog}>удалить</button>
                   </SidebarChatGroup>
                </div>
                <div className='sidebar__bg'></div>
             </div>
          )
       }
    }
    Ответ написан
    1 комментарий
  • Функция не видет обновление стейта Reacta?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    setState - ассинхронная функция. Можно использовать колбек:
    this.setState({
      columnId,
    }, this.fetchData);

    но это не лучшее решение с точки зрения архитектуры, если вам надо вызывать fetchData по изменению разных частей state (пагинация, сортировка, количество элементов на странице и пр.).
    В этом случае fetchData лучше вызывать в componentDidUpdate:
    componentDidUpdate(_, prevState) {
      if (prevState.columnId !== this.state.columnId) {
        this.fetchData();
      }
    }

    Так все условия вызова fetchData по изменению состояния будут находиться в одном месте. Вместо кучи колбеков по функциям компонента. Но если собираетесь дергать только по onSortChange, то можно вызывать fetchData в колбеке setState.
    Ответ написан
    Комментировать
  • Как передать параметр state в другой компонент?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Можно сделать так:
    class FormWidget extends Component {
      state={
        isFormVisible: false,
      };
    
      handleButtonClick = () => {
        this.setState(prevState => ({
          isFormVisible: !prevState.isFormVisible,
        }));
      }
    
      render() {
        const { isFormVisible } = this.state;
        const buttonText = isFormVisible ? 'Hide form' : 'Show form';
    
        return (
          <Wrapper>
            <Button onClick={this.handleButtonClick}>
              {buttonText}
            </Button>
            <Form isVisible={isFormVisible} />
          </Wrapper>
        )
      }
    }


    state из кнопки и формы следует убрать.
    Конструкторы в react использовать не надо.
    Ответ написан
    1 комментарий