Ответы пользователя по тегу React
  • Reducer вызывается по 2 раза в useReducer?

    @paoluccio
    switch (action.type) {
      // ...
      case 'checked':
        return state.map(item => {
          if (item.id === action.id) {
            return { ...item, checked: !item.checked };
          }
          return item;
        });
      // ...
    }
    Ответ написан
    7 комментариев
  • Как сделать так, чтобы у на одной странице менялось только содержимое в зависимости от роутера?

    @paoluccio
    Можно так:
    const PageLayout = ({ children }) => (
      <React.Fragment>
        <header>Header</header>
        {children}
        <footer>Footer</footer>
      </React.Fragment>
    );
    
    const Page = () => (
      <PageLayout>
        <Switch>
          <Route path='/my' component={My} />
          <Route path='/payments' component={Payments} />
        </Switch>
      </PageLayout>
    );
    Ответ написан
    Комментировать
  • Как прокинуть данные полученные с сервера в форму редактирования?

    @paoluccio
    Думаю, что опираться на три точки(список продуктов, один продукт, еще и локальный стейт для редактируемого продукта) с идентичными данными крайне излишне, т.к. создаётся путаница с синхронизацией. Считаю, что нужно опираться только на список продуктов из редакса и доставать из него нужные данные через селекторы. Для редактирования можно использовать временный локальный стейт, который при желании можно сбросить и вернуться к исходным данным.

    Имена немного изменил, чтобы было более понятно. Но вы, если что, спрашивайте, постараюсь помочь.

    // Component
    const ProductEdit = () => {
      const [editableData, setEditableData] = useState({});
    
      const { id } = useParams();
      // пытаемся достать продукт по id из списка
      const selectedProduct = useSelector(state => (
        state.products.find(product => product.id === id)
      ));
    
      useEffect(() => {
        // если продукт был найден, сразу заносим его в локальный стейт
        if (selectedProduct) {
          setEditableData(selectedProduct);
        }
        // иначе получаем данные с бэкэнда(минуя редакс) и затем заносим в локальный стейт
        // этот кейс будет срабатывать в случае если страница была перезагружена
        else {
          API.fetchProductById(id).then(fetchedProduct => setEditableData(fetchedProduct));
        }
      }, [selectedProduct, id]);
    
      const onChange = ({ target: { name, value } }) => {
        setEditableData(currentData => ({ ...currentData, [name]: value }));
      };
    
      const dispatch = useDispatch();
      const onSubmit = e => {
        e.preventDefault();
        dispatch(updateProduct({ id, ...editableData }));
      };
    
      return (
        <form onSubmit={onSubmit}>
          <input type='text' name='name' value={editableData.name || ''} onChange={onChange} />
          <input type='text' name='text' value={editableData.text || ''} onChange={onChange} />
          <input type='text' name='price' value={editableData.price || ''} onChange={onChange} />
          <button>update product</button>
        </form>
      );
    };

    // Thunk
    const updateProduct = product => async (dispatch, getState) => {
      try {
        await API.updateProduct(product);
        // проверяем на наличие списка продуктов, может не быть если страница перезагружалась
        if (getState().products.length) {
          dispatch(mergeUpdatedProduct(product));
          // ловим type этого экшна в редьюсере и перезаписываем старый продукт в списке
        }
        // если же список пустой, то при возврате на страницу показа продуктов заново получаем все данные
        // к тому моменту обновлённый продукт уже успешно будет проживать в бд
      } catch (error) {
        // не удалось обновить продукт
      }
    };
    Ответ написан
  • Можно ли хуки вынести в отдельную функцию. И если нет, то как быть?

    @paoluccio
    И пытаюсь вызвать ее из pushForm()

    это против правил, хуки должны жить на верхнем уровне в компонентах или же других(кастомных) хуках.

    В вашем случае, как один из вариантов, логику обработки формы можно вынести в кастомный хук. Затем использовать его в LoginUser.
    Приблизительная структура:
    // Custom hook
    export const useAuthHandler = () => {
      const [token, setToken] = useState(null);
    
      useEffect(() => {
        if (token) { // чтобы не диспатчить с начальным null на первом рендере
          // диспатчим экшн с полученным токеном
        }
      }, [token]); // будет следить за token
    
      return () => { // возвращаем функцию-обработчик
        // запрашиваем токен, в then вызываем setToken(token)
      };
    };
    
    // Component
    export default function LoginUser() {
      const handleSubmit = useAuthHandler();
    
      return (
        <div>
          <form id="auth_form">
            {/* ... */}
            <Button autofocus="true" onClick={handleSubmit}>Авторизоваться</Button>
          </form>
        </div>
      );
    };
    Ответ написан
    Комментировать
  • Как передать данные между двумя компонентами React (не родителем и потомком)?

    @paoluccio
    Можно попробовать динамически вставлять index в путь для Link:
    <Link to={`/edit/${index}`}>Edit</Link>

    Роут тоже слегка изменить:
    <Route path='/edit/:index'>
      <EditPage />
    </Route>


    Затем, на странице редактирования todo, index можно получить через хук useParams:
    import { useParams } from 'react-router-dom';
    
    const EditPage = () => {
      const { index } = useParams();
    
      // ...
    };
    Ответ написан
  • Как реализовать запись в state из props в функциональном компоненте?

    @paoluccio
    Можно попробовать синхронизировать локальный стэйт с данными из редакса. Структура, приблизительно, будет следующей:
    const Form = props => {
      const [localData, setLocalData] = useState();
    
      useEffect(() => {
        setLocalData(props.reduxData);
      }, [props.reduxData]);
    
      return (
        // ...
      );
    };


    Далее, в блоке return, через onChange на полях вашей формы, обновляете локальный стэйт. По сабмиту формы или еще как-нибудь, диспатчите экшн с актуальными данными из локального стэйта.
    Ответ написан
    1 комментарий
  • Почему редюсер вызывается два раза?

    @paoluccio
    но при создании функции отметки дела галочкой...

    у вас там ошибка, вы мутируете todo, что не есть хорошо...

    Должно быть:
    export const todoReducer = (state, action) => {
      switch (action.type) {
        // ...
        case "MARK_TODO":
          const newState = state.map(todo => {
            if (todo.id === action.id) {
              return { ...todo, status: !todo.status };
            }
            return todo;
          });
          return newState;
        // ...
      }
    };


    ...запускается будто "из воздуха".

    Будто любое действие кроме 'ADD_TODO' заранее проклято.


    Имя злодею - Strict Mode. Почитать можно тут.

    Если сильно раздражает - идёте в index.js и убираете обёртку над <App />.
    Ответ написан
    1 комментарий
  • React: Как перебросит данные из объекта на другой компонент?

    @paoluccio
    Можно поместить data в контекст, ну и обращаться где захочется с помощью useContext.

    И тогда получится что-то такое:
    const AppMessageItems = () => {
      const { id } = useParams(); // react-router-dom hook
      const subset = useContext(YourContextName).find(item => item.id === id);
    
      return (
        <Container>
          <div> Id: {subset.id}</div>
          <div> Text: {subset.text}</div>
          ... 
        </Container>
      );
    };
    Ответ написан
    Комментировать
  • Почему не работает React Router?

    @paoluccio
    Ваш код работает
    Ошибка в чём-то другом. Попробуйте открыть ваше приложение в режиме инкогнито или обновите react-router-dom.
    Ответ написан
    4 комментария