Как передать состояние между функциональными компонентами?

В настоящее время я пишу страницу регистрации с помощью react-js с реагирующими хуками, и я все еще учусь, поэтому, пожалуйста, извините меня, если это очень простой вопрос.

У меня есть signup.js, написанный в функциональном компоненте с хуками. signup.js импортирует 'EmailTextField', 'PasswordTextField', 'NameTextField', 'CellPhoneTextField'... компоненты которых также прописаны в функциональных компонентах с хуками.

Я сделал все эти текстовые поля отдельными компонентами, чтобы упростить код, поскольку у меня есть требование иметь много разных проверок для каждого текстового поля. (и наличие всех этих полей на странице signup.js делает очень длинный код)

В конце процесса в signup.js я хотел бы получить состояние всех его подкомпонентов (всех этих текстовых полей) (независимо от того, подходит ли пользователь для входа или нет.), но я не уверен, как пройти состояние (или переменная) от этих текстовых полей до signup.js.

Я знаю, что избыточность может управлять состоянием, но можно ли добиться этого без избыточности?

Спасибо.

Я создал Образец CodeSandbox с минимальным примером кода для работы.

Здесь я использую компонент EmailTextfield в apptest.js. Я хотел бы получить состояние isValid на EmailTextfield от apptest.js, чтобы я мог убедиться, что все поля проверены до того, как пользователь зарегистрируется.

'./components/UI/Textfield/EmailTextField.js'

import React, { useState } from "react";
import TextField from "@material-ui/core/TextField";
import Grid from "@material-ui/core/Grid";

export const EmailTextField = props => {
  const [value, setValue] = useState("");
  const [helperText, setHelperText] = useState(
    "Email address will be used as your username."
  );
  const [isValid, setIsValid] = useState("true");

  const handleOnChangeEmailAddress = event => {
    // Email Validation logic
    if (true) {
      setIsValid(true);
    } else {
      setIsValid(false);
    }
  };

  return (
    <Grid item xs = {12}>
      <TextField
        variant = "outlined"
        required
        fullWidth
        id = "email"
        label = "email address"
        error = {!isValid}
        helperText = {helperText}
        name = "email"
        autoComplete = "email"
        margin = "dense"
        onBlur = {handleOnChangeEmailAddress}
      />
    </Grid>
  );
};

export default EmailTextField;

'aptest.js'

import React from "react";
import CssBaseline from "@material-ui/core/CssBaseline";
import Grid from "@material-ui/core/Grid";
import { makeStyles } from "@material-ui/core/styles";
import Container from "@material-ui/core/Container";
import { EmailTextField } from "./components/UI/Textfield/EmailTextField";

const useStyles = makeStyles(theme => ({
  "@global": {
    body: {
      backgroundColor: theme.palette.common.white
    }
  },
  paper: {
    marginTop: theme.spacing(8),
    display: "flex",
    flexDirection: "column",
    alignItems: "center"
  },
  mainBox: {
    // margin: '200px',
    width: "550px",
    textAlign: "left",
    boxShadow: "0 2px 3px #ccc",
    border: "1px solid #eee",
    padding: "40px 70px 50px 70px",
    boxSizing: "border-box"
  },
  form: {
    width: "100%", // Fix IE 11 issue.
    marginTop: theme.spacing(3)
  }
}));

const Apptest = props => {
  const classes = useStyles();
  return (
    <Container component = "main" maxWidth = "xs">
      <CssBaseline />
      <div className = {classes.paper}>
        <div className = {classes.mainBox}>
          <form className = {classes.form} noValidate>
            <Grid container spacing = {2}>
              <EmailTextField />
            </Grid>
          </form>
        </div>
      </div>
    </Container>
  );
};

export default Apptest;

не могли бы вы добавить пример кода или ссылку на codeandbox?

Claeusdev 16.07.2019 02:08

Я загрузил пример кода в codeandbox.io/embed/cranky-glade-82leu

hinewwiner 16.07.2019 03:39

В этом примере у меня есть только один компонент с именем EmailTextField.js. Я хотел бы получить состояние «IsValid» от компонента на apptest.js.

hinewwiner 16.07.2019 03:41
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
1
3
3 761
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Я имею в виду очень грубую реализацию.

Вокруг полей ввода должна быть согласованная модель данных. Эта модель данных должна быть единственным источником правды для этого конкретного поля ввода. Он должен быть в состоянии сказать, было ли затронуто это конкретное поле, есть ли ошибки, является ли оно нетронутым, какова его ценность и все такое прочее.

Итак, скажем, у вас это так:

errors: [],
onChange: false,
pristine: true,
touched: false,
value,

Назовем это StateChangeEvent.

Теперь каждое поле ввода будет иметь обработчик таких событий, как изменение и размытие. Здесь этот отдельный компонент будет обновлять StateChangeEvent. И эти методы в конечном итоге вызовут функцию обратного вызова с StateChangeEvent в качестве аргумента.

Таким образом, родитель узнает, что в одном из полей произошло изменение, и сможет соответствующим образом отреагировать.

В родительском компоненте, чтобы активировать кнопку «Отправить» в форме, мы также можем иметь побочный эффект, который будет обновлять общее состояние формы. Что-то вроде этого:

useEffect(() => {
  const isValid = !fieldOne.onChange &&
    fieldOne.errors.length === 0 &&
    fieldOne.value.length !== 0 &&
    !fieldTwo.onChange &&
    fieldTwo.errors.length === 0 &&
    fieldTwo.value.length !== 0 &&
    ...;
  setIsFormValid(isValid);
}, [fieldOne, fieldTwo, ...]);

Я уверен, что это не полное решение. Но я уверен, что это поможет вам начать.

Обновлено:

Основываясь на предоставленной вами CodeSandbox, вот что вы можете сделать, чтобы это заработало:

import ...

const useStyles = makeStyles(theme => ({ ... }));

const Apptest = props => {

  const classes = useStyles();
  const [isInvalid, setIsInvalid] = useState(true);

  const handleStateChange = updatedState => {
    console.info("updatedState: ", updatedState);
    updatedState.errors.length === 0 ? setIsInvalid(false) : setIsInvalid(true);
  };

  return (
    <Container component = "main" maxWidth = "xs">
      <CssBaseline />
      <div className = {classes.paper}>
        <div className = {classes.mainBox}>
          <form className = {classes.form} noValidate>
            <Grid container spacing = {2}>
              <EmailTextField onStateChange = {handleStateChange} />
            </Grid>
            <Button
              variant = "contained"
              color = "primary"
              disabled = {isInvalid}
              className = {classes.button}
            >
              Submit
            </Button>
          </form>
        </div>
      </div>
    </Container>
  );
};

export default Apptest;

И в компоненте EmailTextField:

import React, { useState } from "react";
import TextField from "@material-ui/core/TextField";
import Grid from "@material-ui/core/Grid";

export const EmailTextField = props => {
  const { onStateChange } = props;
  const [state, setState] = useState({
    errors: [],
    onChange: false,
    pristine: true,
    touched: false,
    value: null
  });
  const helperText = "Email address will be used as your username.";

  const handleBlur = event => {
    // Email Validation logic
    const matches = event.target.value.match(
      `[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,3}`
    );
    if (matches) {
      const updatedState = {
        ...state,
        touched: true,
        value: event.target.value,
        errors: []
      };
      setState(updatedState);
      onStateChange(updatedState);
    } else {
      const updatedState = {
        ...state,
        touched: true,
        value: event.target.value,
        errors: ["Please enter a valid email"]
      };
      setState(updatedState);
      onStateChange(updatedState);
    }
  };

  return (
    <Grid item xs = {12}>
      <TextField
        variant = "outlined"
        required
        fullWidth
        id = "email"
        label = "email address"
        error = {state.errors.length > 0}
        helperText = {state.errors.length > 0 ? state.errors[0] : helperText}
        name = "email"
        autoComplete = "email"
        margin = "dense"
        onBlur = {handleBlur}
      />
    </Grid>
  );
};

export default EmailTextField;

Here's a Working CodeSandbox Sample for your ref.

Я загрузил свой пример кода в codeandbox.io/embed/cranky-glade-82leu

hinewwiner 16.07.2019 03:39

В этом случае, как я могу получить состояние «isValid» в component/UI/testfield/EmailTextfield.js из aptest.js?

hinewwiner 16.07.2019 03:40

Я видел ваш ответ, но было неясно, как я буду передавать StateChangeEvent туда и обратно между компонентами.

hinewwiner 16.07.2019 03:41

Я разобрался, извините за поздний ответ. Я спал. По сути, onBlur() принимает обратный вызов, теперь в этом случае вам нужно передать значение в поле ввода обратному вызову, чтобы вы могли получить доступ к значению пользовательского ввода. Другой способ — использовать onChange() для отслеживания изменения и настроить его так, чтобы при вызове onblur вы могли проверить value, а затем выполнить проверки.

Таким образом, вам просто нужно передать target значение события в callback вот так onBlur = {(e) => handleOnChangeEmailAddress(e.target.value)}, и тогда вы сможете получить доступ к значению в методе. Я провел рефакторинг кода, которым вы поделились в песочнице. Найдите ниже фрагмент того, что я сделал.

import React, { useState } from "react";
import TextField from "@material-ui/core/TextField";
import Grid from "@material-ui/core/Grid";

export const EmailTextField = props => {
  const [value, setValue] = useState("");
  const [helperText, setHelperText] = useState(
    "Email address will be used as your username."
  );
  const [isValid, setIsValid] = useState("true");

  const handleOnChangeEmailAddress = value => {
    // Email Validation logic
    if (!value) {
      setIsValid(true);
    } else {
      setIsValid(false);
    }
    console.info(isValid)
  };

  return (
    <Grid item xs = {12}>
      <TextField
        variant = "outlined"
        required
        fullWidth
        id = "email"
        label = "email address"
        error = {!isValid}
        helperText = {helperText}
        name = "email"
        autoComplete = "email"
        margin = "dense"
        onBlur = {(e) => handleOnChangeEmailAddress(e.target.value)}
      />
    </Grid>
  );
};

export default EmailTextField;

Я надеюсь, что это поможет .. если у вас есть какие-либо проблемы, не стесняйтесь задавать вопросы ..

Из вашего примера codeandbox похоже, что вы были почти там, вам просто нужно было передать свою функцию onStateChange в качестве опоры:

<EmailTextField onStateChange = {onStateChange} />

Затем реализуйте функцию onStateChange в файле apptest.js, который получит обновленный объект.

Посмотрите мой пример ниже и откройте консоль, вы увидите журналы консоли для ошибок и ответ «isValid», если адрес электронной почты действителен.

https://codesandbox.io/s/loving-blackwell-nylpy?fontsize=14

Спасибо большое. Я следовал решению SiddAjmera (как вы и сказали), и оно работает!

hinewwiner 16.07.2019 09:09

не беспокойтесь @hinewwiner рад, что вы смогли заставить его работать

Jon B 24.07.2019 08:12

Другие вопросы по теме