Ответы пользователя по тегу React
  • Можно ли при обработке события submit формы, узнать введённые данные?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    class Example extends Component {
      state = {
        inputValue: '',
      };
    
      handleChange = e => { 
        const { name, value } = e.target;
        
        this.setState({
          [name]: value,
        });
      };
    
      handleSubmit = e => {
        e.preventDefault();
        //  получить состояние формы можно обратившись к this.state
      };
    
      render() {
        const { inputValue } = this.state;
    
        return (
          <form onSubmit={this.handleSubmit}>
            <input
              name="inputValue"
              value={inputValue}
              onChange={this.handleChange}
            />
            ...
          </form>
        );
      }
    Ответ написан
    Комментировать
  • Как реализовать ссылки на пункты из списка, чтобы пункты открывались в отдельном div для редактирования CRUD React?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Cмотря как хотите реализовать. Если хотите открывать на той же странице, то достаточно хандлера, ключа состояния и условия в render:
    class Example extends Component {
      state = {
         eventsList,
        selectedEvent,
      };
    
      handleSelectEvent(event) {
         this.setState({ selectedEvent: event });
      }
      
      render() {
        const { eventsList, selectedEvent } = this.state;
                    
        return (
          <Wrapper>
            {selectedEvent && <EventDetails event={selectedEvent} />}
            <ul>
              {eventsList.map(event => (
                <li
                  key={event.id}
                  onClick={() => this.handleSelectEvent(event)}
                >
                  {event.name}
                </li>
              ))}
            </ul> 
          </Wrapper>
        );
      }
    }


    Если хотите на отдельной странице, то лучше использовать роутер.
    В примере ниже используются компоненты BrowserRouter, Switch, Route, Link из пакета react-router-dom.
    Приложение оборачиваете в BrowserRouter:
    <BrowserRouter>
      <App />
    </BrowserRouter>

    В том месте где хотите рендерить роуты:
    <Switch>
      <Route exact path="/" component={Home} />
      <Route path="/events" component={EventsList} />
      <Route path="/event/:id" component={EventDetails} />
    </Switch>

    В компоненте списка:
    class EventsList extends Component {
      state = {
         eventsList,
      };
    
      render() {
        const { eventsList } = this.state;
                    
        return (
          <Wrapper>
            <ul>
              {eventsList.map(event => (
                <li key={event.id}>
                  <Link to={`/event/${event.id}`}>{event.name}</Link>
                </li>
              ))}
            </ul> 
          </Wrapper>
        );
      }
    }


    В компоненте детализации, в зависимости откуда получаете элементы реализация может отличаться,
    но id элемента можно получить из параметров ссылки так:
    class EventsList extends Component {
      componentDidMount() {
        const elementId = this.props.match.params.id;
      }
      render() {
        ...
      }
    }
    Ответ написан
  • React ,Redux && auth?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    const PrivateRoute = ({ component: Component, isAuth, ...rest }) => (
      <Route {...rest} render={props => (isAuth
          ? <Component {...props} />
          : <Redirect to='/login' />
        )}
      />
    );
    
    const mapStateToProps = state => ({
      isAuth: state.isAuth,
    });
    
    const mapDispatchToProps = {
      authenticated,
    };
    
    export default connect(mapStateToProps, mapDispatchToProps)(PrivateRoute);
    Ответ написан
  • В массиве меняется весь массив с объектами?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Вы передаете ссылку на один и тот же массив всем элементам массива graffik:
    grafik.forEach(elem => {
      elem.attr = attr; 
    });


    Когда передаете напрямую, срабатывает именно потому, что каждый раз создаете новый массив с новыми объектами.

    Вам надо передавать копии массива с копиями вложенных объектов. Сделать это можно так:
    grafik.forEach(elem => {
      elem.attr = attr.map(el => ({ ...el }));
    });

    Либо, если не используете spread оператор:
    grafik.forEach(elem => {
      elem.attr = attr.map(el => Object.assign({}, el));
    });
    Ответ написан
    2 комментария
  • Как передать значения от ребенка родителю в React?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    1. Нельзя использовать this.state в setState, так как setState асинхронный метод и на момент вызова состояние может успеть измениться. Для обновления состояния по текущему надо передавать в setState функцию возвращающую новый объект состояния. В момент вызова первым аргументом туда придет актуальное состояние компонента:
    this.setState(prevState => ({
      tasks: [
        ...prevState.tasks,
        { content, comments: [], active: true},
      ],
    }));

    2. Хандлеры лучше биндить в конструкторе, либо использовать inline class arrow function вместо метода класса:
    bind в конструкторе:
    constructor(props) {
      super(props);
      this.onAddItem = this.onAddItem.bind(this);
    }

    inline class arrow function:
    onAddItem = content => {
      // some stuff
    };


    3. С формами гораздо удобней работать через контролируемые элементы:
    class Example extends Component {
      state = {
        inputValue: '',
      };
    
      handleChange = e => { 
        const { name, value } = e.target;
        
        this.setState({
          [name]: value,
        });
      };
    
      render() {
        const { inputValue } = this.state;
    
        return (
          <Wrapper>
            <input
              name="inputValue"
              value={inputValue}
              onChange={this.handleChange}
            />
            ...
          </Wrapper>
        );
      }
    Ответ написан
    Комментировать
  • Как будет с ES6?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Можно так:
    В компоненте в из которого передаете хандлер определяете его как inline arrow class function:
    class Parent {
      onNoteDelete = note => {
        // some stuff
      };
    }

    Такие функции не теряют контекст. Либо, можно забиндить хандлер в конструкторе, как посоветовали выше.

    Метод render:
    render() {
      const { notes, onNoteDelete } = this.props; 
      
      return(
        <div className="notes-grid" ref="grid">
          {notes.map(note => (
            <Note 
              key={note.id} 
              color={note.color}
              onDelete={() => onNoteDelete(note)}
            >
              {note.text} 
            </Note>
           ))}
        </div>
      )
    }


    Для значений которые не переопределяются лучше использовать const, а не let. Так другие программисты будут сразу видеть, что значение не переопределяется и как следствие ваш код будет читаемей.
    Ответ написан
    Комментировать
  • Как загрузить и хранить фото на сервере (React+Express+Mongo)?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Используйте FormData для отправки файлов:
    const formData = new FormData();
    
    formData.append('file', someFile);
    axios.post('/uploadFile', formData, {
      headers: {
        'Content-Type': 'multipart/form-data',
      },
    });


    На сервере используйте midlleware для обработки multipart/form-data. Вам подойдет: formidable, busboy, multer или любое другое middleware, решающее эту задачу.
    Ответ написан
    3 комментария
  • Почему не вешается обработчик на react компонент?

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

    Если используете синтаксис ES6, используйте импорты и экспорты:
    import React from 'react';

    export default Task;

    Хандлеры лучше биндить не в render, а в конструкторе:
    constructor(props) {
      super(props);
    
      this.state = {
        isOpened: false,
      };
    
      this.clickHandler = this.clickHandler.bind(this);
    }


    Или определяйте хандлер как class field arrow function:
    clickHandler = e => {
      // your stuff
    };

    Такая функция привязана к контексту экземпляра и биндить ее не надо.
    create-react-app поддерживает такой синтаксис из коробки.

    Нельзя передавать текущее состояние в метод setState, так как это асинхронный метод, к моменту вызова состояние может успеть измениться и будет использовано старое значение. Чтобы обновить состояние на основе предыдущего передавайте в setState функцию возвращающую объект. При вызове, первым аргументом в нее придет актуальное состояние на момент обновления:
    this.setState(prevState => ({
      isOpened: !prevState.isOpened,
    }));


    Вместо var используйте const для неизменяемых значений и let для изменяемых.

    Наконец, следите за чистотой кода. Вот хорошие гайдлайны от Airbnb:
    Гайдлайн Airbnb по JavaScript
    Гайдлайн Airbnb по React
    Ответ написан
    6 комментариев
  • Вопрос по документации Reactjs Lists and Keys?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Вы наверно не заметили, но массив в примерах называется numbers, а аргумент колбека в который передается элемент массива number.

    Исходные данные:
    const numbers = [1, 2, 3, 4, 5];
    const listItems = numbers.map((number) =>
        <li key={number.toString()}>
          {number}
        </li>
      );


    Результат выполнения метода map, в виде JSX:
    [
      <li key="1">1</li>,
      <li key="2">2</li>,
      <li key="3">3</li>,
      <li key="4">4</li>,
      <li key="5">5</li>,
    ]

    Результат в виде JS:
    5a8201cac8298094680322.png

    После рендера массива:
    <ul>
      {listItems}
    </ul>

    получаем следующий html:
    <ul>
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
      <li>5</li>
    </ul>


    О методе массива map можете почитать тут.
    Ответ написан
    Комментировать
  • Почему props меняется сразу для всех, а не для каждого отдельно, react/redux?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Можно сделать isLoading и хранилище сущностей объектом:
    const initialState: {
      entities: {}.
      isLoading: {
        'ALL': false, 
      },
       isError: {
        'ALL': false, 
      }
    }


    const someEntityReduser = (state = initalState, action) => {
      const { type, payload } = action;
    
      switch (type) {
        case GET_ALL_ENTITIES_REQUEST:
          return {
            ...state,
            isLoading: {
              ...state.isLoading,
              'ALL': true, 
            },
            isError: {
              ...state.isError,
              'ALL': false, 
            },
          };
    
        case GET_ALL_ENTITIES_SUCCESS:
          return {
            ...state,
            entities: payload,  // либо нормализация если приходят массивом
            isLoading: {
              ...state.isLoading,
             'ALL': false,
            },
          };
    
        case UPDATE_ENTITY_REQUEST:
          return {
            ...state,
            isLoading: {
              ...state.isLoading,
              [payload.id]: true, 
            },
            isError: {
              ...state.isError,
              [payload.id]: false, 
            },
          };
          
        case UPDATE_ENTITY_SUCCESS:
          return {
            ...state,
            entities: {
               ...state.entities,
               [payload.id]: payload,  
            },
            isLoading: {
              ...state.isLoading,
              [payload.id]: false,
            },
          };
        
         /* other cases */
      }
    }
    Ответ написан
  • Как при первой загрузке страницы отображать не дефолтный роут?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Если вы имели ввиду первое посещение сайта с конкретного браузера, то для решения этой задачи можно использовать localstorage:
    import React, { Component } from 'react';
    import { Redirect } from 'react-router-dom';
    
    const isVisited = localStorage.getItem("isVisited");
    
    if (!isVisited) {
      localStorage.setItem("isVisited", true);
    }
    
    
    class Home extends Component {
      ...
      render() {
        if (!isVisited) return <Redirect to="/nobel-laureates" />
        ...
      }
    }
    Ответ написан
    Комментировать
  • Можно ли менять state внешнего компонента через props?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    1. Если попытаетесь изменить this.props, получите исключение:
    Cannot assign to read only property
    2. У вас есть возможность мутировать state, вот только делать это не нужно.

    Хороший способ управления состоянием компонентов через родителя:
    class Example extends Component {
      state = {
        inputValue: '',
      };
    
      handleChange = e => { 
        const { name, value } = e.target;
        
        this.setState({
          [name]: value,
        });
      };
    
      render() {
        const { inputValue } = this.state;
    
        return (
          <Wrapper>
            <input
              name="inputValue"
              value={inputValue}
              onChange={this.handleChange}
            />
            ...
          </Wrapper>
        );
      }
    Ответ написан
    7 комментариев
  • Как получить nextState, приходит в this.state прошлое значение state?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    setState асинхронная функция и в нее можно передать колбек вторым аргументом:
    this.setState({ ... }, () => this.getStateValues());


    По-хорошему вам следует переписать хандлер, так как вы создаете новые копии каждый рендер, тем более вы передаете свойство name в инпуты, но почему-то не используете в хандлере.
    Перепишите так:
    handleChange = ev => {
       const { name, value } = ev.target;
       const symbols = split('')(value);
       const result = filter(i => characterVerification(i), symbols);
    
       this.setState({
         [name]: join('')(result)
        }, () => this.getStateValues(),
      );
    };


    Еще непонятно зачем было тянуть в проект ramda.
    Ответ написан
  • Странное поведение React. Состояние дочерних элементов то само прокидывается, то нет. Почему?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Вам бы основы JavaScript подтянуть.
    Во-первых вы биндите контекст хандлера на AppField в конструкторе:
    constructor(props) {
      super(props);
      this.state = props.params;
      this.onFieldChange = this.onFieldChange.bind(this); // тут
    }

    Во-вторых, вы определяте переменную self, ссылающуюся на this и она ни делает ровным счетом ничего, так как вы передаете функцию в колбек обработчика события:
    renderSingleValue(data = {}) {
      ...
      let self = this;  // бессмысленно
    
      return (
        <input type="text"
          ...
          onChange={self.onFieldChange} 
          ...
        />
      );
    }

    Даже если бы функция не была забинжена на AppField, у вас бы все-равно ничего не получилось.

    В-третих, значение инпутов лучше хранить в родителе, а не в самих инпутах.

    Трюк с self работает в отложенных вызовах:
    constructor() {
      const self = this;
    
      node.addEventListener('click', function() {
        self.handleEvent();  // сработает, так как функция вызывается на self
      });
    }

    и не сработает при передаче функции:
    constructor() {
      const self = this;
    
      node.addEventListener('click', self.handleEvent);  // не сработает, функция передается
                                                         // в колбек обработчика события
    }                                                    // и не будет вызываться на self


    Пример как можно сделать контролируемую форму с состоянием инпутов в родителе:
    class Example extends Component {
      state = {
        inputValue: '',
      };
    
      handleChange = e => {  // arrow class field function биндится на контекст экземпляра
        const { name, value } = e.target;
        
        this.setState({
          [name]: value,
        });
      };
    
      render() {
        const { inputValue } = this.state;
    
        return (
          <Wrapper>
            <input
              name="inputValue"
              value={inputValue}
              onChange={this.handleChange}
            />
            ...
          </Wrapper>
        );
      }
    }

    Аналогичное решение без использования class field function:
    class Example extends Component {
      constructor(props) {
        super(props);
        this.state = {
          inputValue: '',
        };
        this.handleChange = this.handleChange.bind(this);
      }
      
    
      handleChange(e) {
        const { name, value } = e.target;
        
        this.setState({
          [name]: value,
        });
      }
    
      render() {
        const { inputValue } = this.state;
    
        return (
          <Wrapper>
            <input
              name="inputValue"
              value={inputValue}
              onChange={this.handleChange}
            />
            ...
          </Wrapper>
        );
      }
    }


    Если делаете совой компонент вроде кастомного select, то вы можете в его реализации по изменению сами вызывать хандлер onChange, передавая туда фейковое событие с нужными вам ключами:
    handleChange = value => {
      const { name, onChange } = this.props;
      const fakeEvent = { target: { name, value } };
    
      onChange(fakeEvent);
    };
    Ответ написан
    Комментировать
  • Зачем в React так записывают в state?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Во-первых в setState в корень предыдущее состояние передавать не нужно, так под капотом к нему так и так подмешается предыдущее состояние, в не зависимости от того, что вы туда передадите. Примерно так:
    state = { ...prevState, ...newState };

    Во-вторых не правильно передавать в setState this.state, так как setState выполняется асинхронно и ваше текущее состояние может успеть измениться. Если надо обновить состояние на основе предыдущего, в setState по-хорошему надо передавать функцию, в которую первым аргументом при вызове придет ваше состояние:
    this.setState(prevState => ({
      some_data: {
        ...prevState.some_data,
        ...input,
      },
    });
    Ответ написан
    3 комментария
  • Почему не могу использовать await в if блоке?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Дело не в условном операторе, а в том, что у вас колбек у map не асинхронная функция и в ней нельзя использовать ключевое слово await. Исправить можно так:
    export const renderNotes = (user , users) => dispatch => {
      Object.keys(users).forEach(async element => {
        if (user === users[element].login) {
          const elements = await fire.database().ref('users/' + element + '/notes/'); 
    
          return dispatch({
            type: RENDER_NOTES,
            payload: elements,
          });
        }
      })
    };


    В случае когда надо лишь пройтись по элементам массива, использовать метод map неправильно, для таких случаев есть метод forEach.

    map следует использовать тогда, когда на основе исходного массива, надо получить новый массив элементов, преобразованных по определенному алгоритму:
    const srcArray = [ 0.134555, 0.294587, 0.570858];
    
    const mappedArray = srcArray.map(el => (el * 100).toFixed(1) + '%');
    
    console.log(mappedArray);
    
    // => ["13.5%", "29.5%", "57.1%"]
    Ответ написан
    Комментировать
  • Почему возвращает undefined is not an object (evaluating 'this.props.users')?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Вы передаете функию колбеком в слушатель события onClick, она теряет контекст и this больше не ссылается на ваш компонент.
    Исправить можно, изменив хандлер с class method на inline class function:
    submitBalance = () => {
      // your stuff
    };

    либо можно забинтить функицю в конструкторе:
    constructor(props) {
      super(props);
      this.submitBalance.bind(this);
    }


    Так же можно обернуть this.submitBalance в стрелочную функцию в render, но этот вариант лучше использовать когда надо передать свои аргументы:
    onClick={e => this.submitBalance(someArg, e)}
    Ответ написан
    1 комментарий
  • Нужны ли props?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    Если не используете props, не пишите.
    Когда используете, можно делать деструктуризацию:
    function Welcome({ name }) {
      return <h1>Hello, {name}</h1>;
    }


    Второй вопрос:
    export default function Welcome() {
      return <h1>Hello, World!</h1>;
    }
    Ответ написан
    Комментировать
  • Для чего react server side rendering?

    rockon404
    @rockon404 Куратор тега React
    Frontend Developer
    1. SSR нужен в проектах для которых важна индексация поисковиками.
    2. Такие приложения называют не SPA, а изоморфными.
    3. SSR в изоморфных приложениях используется с REST API, так что проблем с мобильным клиентом у вас не возникнет.
    4. Рендерится все тоже самое, что и в SPA только на стороне сервера, после передачи клиенту приложение работает как обычное SPA и взамодействует с сервером по REST API.
    Ответ написан
    Комментировать