Как обновить состояние компонента, который использует двух потребителей контекста - PullRequest
3 голосов
/ 25 сентября 2019

У меня есть компонент класса, который использует два контекста со значением, сгенерированным из вызовов API к REST API.

Что я хочу сделать, это получить значения контекста и использовать их для обновления состояния моего компонента.

Я передаю значения контекста следующим образом

 <TextContext.Consumer>
        {(textContext) => (
          <UserContext.Consumer>

            {(userConsumer) => {
              const text = textContext.text;
              const user = userConsumer.user;

              if(text != null && user != null){
                return (

                  <div className="md:flex max-w-2xl">

                    <div className="flex flex-col flex-1 md:pr-32">

                      <FuseAnimateGroup
                        enter={{
                          animation: "transition.slideUpBigIn"
                        }}
                      >
                        <div style={{paddingRight:"8px"}}>
                          <Typography variant="h4" >{text.TITLE_PAGE_PROFILE}</Typography>
                          <TextField
                            id="outlined-full-width"
                            style={{ margin: 8 }}
                            placeholder={text.PROFILE_EMAIL_PLACEHOLDER}
                            value = {user.email}
                            disabled
                            fullWidth
                            margin="normal"
                            variant="outlined"
                            InputProps={{
                              endAdornment: (
                                <InputAdornment>
                                  <IconButton>
                                    <EmailIcon/>
                                  </IconButton>
                                </InputAdornment>
                              )
                            }}
                          />
                        </div>


                        <div style={{paddingRight:"8px"}}>
                          <form className={classes.container} noValidate autoComplete="off">
                            <TextField
                              id="outlined-full-width"
                              style={{ margin: 8 }}
                              placeholder={text.PROFILE_NAME}
                              value={user.name_user}
                              fullWidth
                              margin="normal"
                              variant="outlined"
                              InputProps={{
                                endAdornment: (
                                  <InputAdornment position="start">
                                    <AccountCircle />
                                  </InputAdornment>
                                )
                              }}
                            />


                          </form>
                        </div>
                        <div style={{paddingRight:"8px"}}>
                          <TextField
                            id="outlined-full-width"
                            style={{ margin: 8 }}
                            value={user.address_user}
                            placeholder={text.PROFILE_ADDRESS_PLACEHOLDER}
                            fullWidth
                            margin="normal"
                            variant="outlined"
                            InputLabelProps={{
                              shrink: true,
                            }}
                          />
                        </div>

                        <div style={{paddingRight:"8px"}}>
                          <form className={classes.container} noValidate autoComplete="off">
                            <TextField
                              id="outlined-full-width"
                              style={{ margin: 8 }}
                              value={user.city_user}
                              label={text.PROFILE_CITY_PLACEHOLDER}
                              className={classes.textField}
                              fullWidth
                              margin="normal"
                              variant="outlined"
                              InputProps={{
                                endAdornment: (
                                  <InputAdornment position="start">
                                    <LocationCityIcon/>
                                  </InputAdornment>
                                )
                              }}
                            />


                          </form>
                        </div>

                        <div>
                          <TextField
                            id="outlined-select-currency"
                            select
                            value={user.country_user}
                            label={text.PROFILE_COUNTRY_PLACEHOLDER}
                            InputProps={{
                              endAdornment: (
                                <InputAdornment>
                                  <IconButton>
                                    <FlagIcon/>
                                  </IconButton>
                                </InputAdornment>
                              )
                            }}
                            fullWidth
                            style={{ margin: 8, paddingRight: 8}}
                            SelectProps={{
                              MenuProps: {
                                className: classes.menu,
                              },
                            }}


                            margin="normal"
                            variant="outlined"
                          />
                        </div>
                        <div style={{padding:"10px"}}>
                          <Fab variant="contained" aria-label="delete" className={classes.fab}>

                            {text.PROFILE_CHANGE_PASSWORD_BUTTON_PLACEHOLDER}
                          </Fab>
                        </div>

                        <div style={{paddingRight:"8px"}}>
                          <Typography variant="h4" > {text.COMPANY_INFORMATION_TITLE}</Typography>
                          <TextField
                            id="outlined-full-width"
                            style={{ margin: 8 }}
                            placeholder={text.COMPANY_NAME_PLACEHOLDER}
                            value={user.name_company}
                            fullWidth
                            margin="normal"
                            variant="outlined"
                            InputLabelProps={{
                              shrink: true,
                            }}
                          />
                        </div>

                        <div style={{paddingLeft:"10px"}}>
                          <form className={classes.container} noValidate autoComplete="off">
                            <TextField
                              style={divStyle}
                              id="outlined"
                              label={text.COMPANY_EU_VAT_PLACEHOLDER}
                              value={user.vat_company}

                              className={classes.textField}
                              margin="normal"
                              variant="outlined"
                            />

                            <TextField
                              style={div2Style}
                              id="outlined"
                              label={text.COMPANY_NUMBER_PLACEHOLDER}
                              value={user.registration_number_company}
                              className={classes.textField}
                              margin="normal"
                              variant="outlined"
                            />
                          </form>
                        </div>
                        <div style={{paddingRight:"8px"}}>
                          <TextField
                            id="outlined-full-width"
                            style={{ margin: 8 }}
                            value={user.address_company}
                            placeholder={text.COMPANY_ADDRESS_PLACEHOLDER}
                            fullWidth
                            margin="normal"
                            variant="outlined"
                            InputLabelProps={{
                              shrink: true,
                            }}
                          />
                        </div>

                        <div style={{paddingRight:"8px"}}>
                          <form className={classes.container} noValidate autoComplete="off">
                            <TextField
                              id="outlined-full-width"
                              style={{ margin: 8 }}
                              label={text.COMPANY_CITY_PLACEHOLDER}
                              value={user.city_company}
                              className={classes.textField}
                              fullWidth
                              margin="normal"
                              variant="outlined"
                              InputProps={{
                                endAdornment: (
                                  <InputAdornment position="start">
                                    <LocationCityIcon/>
                                  </InputAdornment>
                                )
                              }}
                            />
                          </form>
                        </div>
                        <div>
                          <TextField
                            id="outlined-select-currency"
                            select
                            label={text.COMPANY_COUNTRY_PLACEHOLDER}
                            fullWidth
                            style={{ margin: 8, paddingRight: 8}}
                            SelectProps={{
                              MenuProps: {
                                className: classes.menu,
                              },
                            }}
                            InputProps={{
                              endAdornment: (
                                <InputAdornment>
                                  <IconButton>
                                    <FlagIcon/>
                                  </IconButton>
                                </InputAdornment>
                              )
                            }}

                            margin="normal"
                            variant="outlined"
                          />
                        </div>
                      </FuseAnimateGroup>

                    </div>

                    <div className="flex flex-col md:w-320">
                      <FuseAnimateGroup
                        enter={{
                          animation: "transition.slideUpBigIn"
                        }}
                      >
                        <Button variant="contained"  size="large" color="default" className={classes.button}>
                          {text.UPDATE_BUTTON_TEXT}
                        </Button>

                      </FuseAnimateGroup>
                    </div>
                  </div>
                );
              } else return <div>Loading...</div>
            }
            }

          </UserContext.Consumer>
        )}
      </TextContext.Consumer>

Я пытался обновить состояние внутри рендера, делая что-то вроде этого

  <TextContext.Consumer>
        {(textContext) => (
          <UserContext.Consumer>

            {(userConsumer) => {
              const text = textContext.text;
              const user = userConsumer.user;
              this.setState({
                user:user,
                text: text,
              })

          </UserContext.Consumer>
        )}
      </TextContext.Consumer>

Проблемапри таком подходе он выдает «Максимальная глубина обновления превышена».ошибка.

Как мне поступить?

Ответы [ 3 ]

1 голос
/ 25 сентября 2019

"Превышена максимальная глубина обновления."ошибка.

Не setState() внутри render().

Как мне поступить?

Просто извлеките из него компонент .

const User = (props) => {
  return (
    <>
      <span>{props.user}</span>
      <span>{props.text}</span>
    </>
  );
}

// in render
<TextContext.Consumer>
  {(textContext) => (
    <UserContext.Consumer>
      {(userConsumer) => (
        <User
          text={textContext.text}
          user={userConsumer.user}
        />
      ))}
    </UserContext.Consumer>
  )}
</TextContext.Consumer>

<User /> будет по-прежнему перерисовываться каждый раз, когда изменяется реквизит (пользователь, текст).

0 голосов
/ 25 сентября 2019

Прежде всего - вы уверены, что вам действительно нужно хранить контекст в состоянии?Я не вижу смысла копировать контекст (который всегда доступен) в состояние.Просто используйте значения из контекста, а не из состояния.

Но если вам это действительно нужно, вы не можете обновить состояние в функции рендеринга, потому что это вызовет бесконечный цикл обновления.Для этого есть несколько вариантов:

  • Извлечь компонент:
return (
  <TextContext.Consumer>
    {({ text }) => (
      <UserContext.Consumer>
        ({user}) => <ExtractedComponent text={text} user={user} />
      </UserContext.Consumer>
    )}
  </TextContext.Consumer>
);

Тогда вам просто нужно переопределить getDerrivedStateFromProps() для ExtractedComponent, чтобы получить новое состояние при переходеизменено.

  • [некрасиво] выполнить условное обновление в функции рендеринга для предотвращения бесконечного цикла:
if (state.user !== user || state.text !== text) {
  this.setState({ user, text });
}
  • Возможно, вы можете переключиться на функциональные компоненты с помощью ловушек:
const YourComponent = () => {
  const { user } = useContext(UserContext);
  const { text } = useContext(TextContext);
  const [ state, setState ] = useState({});

  useEffect(() => {
    setState({ user, text });
  }, [ user, text ]);
}
0 голосов
/ 25 сентября 2019

вы не можете обновить состояние внутри функции рендеринга.

Таким образом, вы будете в бесконечном цикле рендеринга.Всякий раз, когда вы изменяете состояние, запускающее функцию рендеринга, вы снова изменяете состояние и т. Д.

В любом случае вам не нужно хранить это состояние внутри локального состояния, чтобы использовать его, вы можете использовать его напрямуюиз контекста.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...