Почему опора дочернего компонента показывает только начальное значение родительского компонента? - PullRequest
1 голос
/ 26 мая 2020

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

Иерархия компонентов:

  1. App.js (обрабатывает вход в систему и маршрутизацию)
  2. <SearchPage /> и <SearchResults />
  3. Есть <SearchNavBar /> и <SearchMain /> под <SearchPage /> (<SearchMain /> содержит форму и данные для отправки пользователями, и после отправки пользователи должны перейти к маршруту, по которому находится <SearchResults />.)

Вот проблема: поскольку реакция разрешает поток данных только в одном направлении, я должен передать ввод пользователя от <SearchMain /> вверх до App.js, а затем сохранить его в состоянии App.js и передать его вниз как опора до <SearchResults /> вместе с токеном, полученным от App.js. Однако даже я обновил состояние в App. js и передал его как опору его дочернему компоненту <SearchResults />, в то время как console.logging реквизиты дочернего компонента <SearchResults />, я мог получить только начальные значения состояния, а не обновленные, почему ??

Код:

Приложение. js:

const myMSALObj = new UserAgentApplication(msalConfig);
class App extends Component {
  constructor(props) {
       super(props)
    this.state = {
      res:'',
      isLoggedIn:false,
      keyword:''
    }        
    this.handleLogOut = this.handleLogOut.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }    
  handleLogOut(){
    myMSALObj.logout();
    this.setState({isLoggedIn:false})
  }   
  handleSubmit (typedValue){
      this.setState({keyword:typedValue},() => console.log(this.state));//here works just fine, showing correct state value
      window.location.replace('/results')
    };

  async componentDidMount(){

    const idToken_response = await myMSALObj.loginPopup(loginRequest);
    const response = await myMSALObj.acquireTokenPopup(loginRequest);

    this.setState({isLoggedIn:true,res:response});
  }
  render(){
    return (
    <Router>
      <div>
        <Switch>
        {this.state.isLoggedIn === true ? 
        <Route exact path="/" render={() => <SearchPage 
                                             parentState = {this.state} 
                                             handleLogOut = {this.handleLogOut}
                                             handleSubmit = {this.handleSubmit} />}/>:
        <Route exact path="/" render={() => (<div>Please wait till you are logged in...</div>)}/>}
        <Route exact path="/results" render={() => <SearchResults 
                                                    parentState = {this.state.keyword} 
                                                    handleLogOut = {this.handleLogOut} />}/>          
        </Switch>
      </div>
    </Router>
    );
  }
}
export default App;

SearchPage:

class SeachPage extends Component{

    constructor (props){
        super (props);
        this.state = {
            // userInfo: this.props,
            accessToken: this.props.parentState.res.accessToken,
            name:this.props.parentState.res.account.name,
            email:this.props.parentState.res.account.userName
        }
        // console.log(this.props)
    }
    render(){
        return (
            <Fragment>
                <SearchNavBar 
                 info={this.state} 
                 handleLogOut = {this.props.handleLogOut}/>
                <SearchMain 
                 accessToken={this.state.accessToken } 
                 handleSubmit = {this.props.handleSubmit}/>
            </Fragment>

        )
    }
}

export default SeachPage;

SearchMain:

class SearchMain extends Component{
    constructor(props){

        super(props);

        this.state = {
            token:this.props.accessToken,
            keyword:'',
            typed:'',
        }

        this.handleChange = this.handleChange.bind(this);
        this.handleSubmitForm = this.handleSubmitForm.bind(this);

        // console.log(this.props)
    }

    handleChange(event) {
        let typed = event.target.value;
        let keyword = event.target.value;
        this.setState({typed: typed,keyword:keyword},() => console.log(this.state));

      };

    handleSubmitForm(event){
        const keyword = this.state.keyword;
        this.props.handleSubmit(keyword);
        event.preventDefault();
    }

    render(){
        return(
            <div className="container" id="searchBox">
            <form onSubmit = {this.handleSubmitForm } className="field is-grouped"> 
                <p className="control is-expanded">
                <input type="text" className="input" value={this.state.keyword} onChange={this.handleChange} autoComplete="off" placeholder="Find an asset" required></input>
                </p>
                <p className="control">
                    <button id="button-in-nav-bar" type="submit" className="button is-primary"><strong>Search</strong></button>
                </p>
            </form>
            </div>
        )
    } 

};

export default SearchMain;

SearchResults

class SearchResults extends Component {

    constructor(props){
        super(props);
        console.log(this.props) //here is the issue!! onlyshowing{res:'',isLoggedIn:false,keyword:''}
    }

    render (){
        return(
            <div>test results</div>
        )
    }
};

export default SearchResults;

Update Итак, вкратце, я смог передать ключевое слово вверх родительскому компоненту, кажется, немного поздно, скриншоты могут лучше описать мою борьбу: App.js console и если я раскомментирую window.location.replace('/results') на первом снимке экрана, страница будет перемещена на '/results', и если I console.log(this.props) в функции рендеринга searchResults.js, реквизиты отображались 4 раза, а последние 2 раза имели частично правильную информацию. enter image description here, все же ключевое слово отсутствовало ... У меня было ощущение, что это может быть b c асинхронной c природы, но я действительно ничего не знаю о h как это исправить .. (побочный вопрос, почему он 4 раза проиграл?)

Ответы [ 2 ]

1 голос
/ 26 мая 2020

Первая проблема

Использование window.replace перезагрузит страницу, и все ваше состояние будет восстановлено до исходных значений. Следовательно, вы потеряете состояние keyword. Таким образом, вам нужно обернуть свой App в Router и использовать history в handleSubmit.

Вторая проблема

В SearchMain, вы получение всего состояния из props в вашем состоянии, то есть внутри конструктора (который выполняется один раз). Следовательно, для компонента SearchMain дальнейшие обновления недоступны. Поэтому просто используйте реквизиты (не используйте состояние)

Обновлено приложение. js

const myMSALObj = new UserAgentApplication(msalConfig);
class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      res: {}, //<--- initialise as an empty object(not string)
      isLoggedIn: false,
      keyword: "",
    };
    this.handleLogOut = this.handleLogOut.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
  handleLogOut() {
    myMSALObj.logout();
    this.setState({ isLoggedIn: false });
  }
  handleSubmit(typedValue) {
    this.setState({ keyword: typedValue }, () => console.log(this.state)); //here works just fine, showing correct state value
    // window.location.replace("/results");
    this.props.history.replace("/results")
  }

  async componentDidMount() {
    const idToken_response = await myMSALObj.loginPopup(loginRequest);
    const response = await myMSALObj.acquireTokenPopup(loginRequest);

    this.setState({ isLoggedIn: true, res: response });
  }
  render() {
    return (
        <div>
          <Switch>
            {this.state.isLoggedIn === true ? (
              <Route
                exact
                path="/"
                render={() => (
                  <SearchPage
                    parentState={this.state}
                    handleLogOut={this.handleLogOut}
                    handleSubmit={this.handleSubmit}
                  />
                )}
              />
            ) : (
              <Route
                exact
                path="/"
                render={() => <div>Please wait till you are logged in...</div>}
              />
            )}
            <Route
              exact
              path="/results"
              render={() => (
                <SearchResults
                  parentState={this.state.keyword}
                  handleLogOut={this.handleLogOut}
                />
              )}
            />
          </Switch>
        </div>
    );
  }
}
export default (props) => (
  <Router>
    <Route render={(rProps) => <App {...props} {...rProps} />} />
  </Router>
);

Обновлено SearchMain. js

class SeachPage extends Component{

    constructor (props){
        super (props);
        // this.state = {
        //     // userInfo: this.props,
        //     accessToken: this.props.parentState.res.accessToken,
        //     name:this.props.parentState.res.account.name,
        //     email:this.props.parentState.res.account.userName
        // }
        // console.log(this.props)
    }

    render(){
        return (
            <Fragment>
                <SearchNavBar 
                 info={this.props} // <----- use props (not state)
                 handleLogOut = {this.props.handleLogOut}/>
                <SearchMain 
                 accessToken={this.props.accessToken } // <----- use props (not state)
                 handleSubmit = {this.props.handleSubmit}/>
            </Fragment>

        )
    }
}

export default SeachPage;
1 голос
/ 26 мая 2020

Значение свойства console.log в методе конструктора => отображение данных - это первые данные. Вы должны попробовать console.log для метода рендеринга. Гудлак!

...