【问题标题】:How to prevent component from rendering child until data fetches如何防止组件在获取数据之前渲染子组件
【发布时间】:2018-05-17 00:48:11
【问题描述】:

我有一个仪表板组件,它使用从后端服务器获取的数据呈现一组卡片。用户可以通过提交表单来创建其他卡片,然后将它们重定向回仪表板页面。

我的问题是,提交表单时,会抛出 javascript 错误“无法读取未定义的属性“包含””,并且仪表板不会呈现。如果我手动刷新页面,列表将按预期使用新卡呈现。我使用 Array.includes 方法根据 filterText 状态值过滤卡片。出现这个错误是因为调用render时没有获取数据吗?如果是这样,我如何强制组件等到有数据再渲染?请参阅下面的组件和 redux 操作。

const CardList = (props) =>  {

    const cards = props.cards.map(({ _id, title}) => {
            return (
                <Card key={_id} title={title} />
            )
        });

        return (
            <div className="container">
                <input onChange={ (e) => props.handleChange(e.target.value) } />
                <div className="row">
                    {cards}
                </div>
            </div>
        );
} 
export default CardList;

export class Dashboard extends Component {
    constructor(props) {
        super(props);
        this.state = {
            filterText: ''
        }
    }
    componentDidMount() {
        this.props.fetchCards();
    } 

    handleChange = (filterText) => {
        this.setState({filterText});
    }

    render() {
        const cardList = this.props.cards.filter(card => 
            card.title.includes(this.state.filterText.trim().toLowerCase())
        );
        return (
            <div>
                <CardList cards={cardList}
                    handleChange={filterText => this.handleChange(filterText)} />
            </div>
        );
    }
};
function mapStateToProps({ cards: { cards }}) {
    return {
        cards,
    }
}
export default connect(mapStateToProps, {fetchCards})(Dashboard);

export class SurveyForm extends Component {
render() {
        return (
            <div>
               <form>
                 <Field component={CardField} type="text" 
                     label={'title'} name={'title'} key={'title'} />
                 <Button type="submit" onClick={() => submitCard(formValues, history)}>Next</Button>
               </form>
            </div>
        );
    }
}
REDUX ACTION DISPATCHER:
export const submitCard = (values, history) => async dispatch => {
    const res = await axios.post('/api/cards', values);

    try {
        dispatch({ type: SUBMIT_CARD_SUCCESS, payload: res.data });
        dispatch({ type: FETCH_USER, payload: res.data })
    }
    catch(err) {
        dispatch({ type: SUBMIT_CARD_ERROR, error: err });
    }

    history.push('/cards');
}

【问题讨论】:

    标签: javascript reactjs redux react-redux redux-form


    【解决方案1】:

    与@JasonWarta 提到的类似,值得注意的是,当返回falsenullundefined 时,React 不会呈现任何内容,因此您通常可以使用&amp;&amp; 比使用更简洁条件(“三元”)运算符:

    render() {
      return this.props.cards && (
        <div>
          <CardList 
            cards={this.props.cards.filter(card => card.title.includes(this.state.filterText.trim().toLowerCase())}
            handleChange={filterText => this.handleChange(filterText)}
          />
        </div>
      );
    }
    

    因为&amp;&amp;短路,后面的部分不会被求值,所以你可以避免TypeErrors,组件也不会渲染任何内容(和你返回null时一样)。

    【讨论】:

    • 如果您不需要渲染任何其他内容,我真的很喜欢这种方法。谢谢,注意到:)
    • 它也可以与其他内容混合使用。如果一个组件有一个为 null/undefined/false 的子组件,它也不会渲染。这使您可以在树中执行内联条件。
    • @Jacob 谢谢,这是有道理的。但是它不起作用,所以我认为问题出在其他地方。查看类型错误,它被抛出在 redux 操作的最后一行。 “history.push('/cards')”。这有什么区别吗?如果有帮助,我也可以编辑以发布减速器。
    • 一旦我们有更多的条件,它就会变得有点不可读,所以另一种方法是在单独的函数中分离出条件片段。
    【解决方案2】:

    我在这种情况下使用了三元运算符。您可能需要调整模式的检查部分,具体取决于您的 redux 模式返回的内容。如果this.props.cards 为假,则返回null 值。

    render() {
        return (
            {this.props.cards
                ?
                    <div>
                        <CardList 
                            cards={this.props.cards.filter(card => card.title.includes(this.state.filterText.trim().toLowerCase())}
                            handleChange={filterText => this.handleChange(filterText)}
                        >
                        </CardList>
                    </div>
                :
                    null
            }
        );
    }
    

    【讨论】:

      【解决方案3】:

      作为其他答案的替代方案,如果您的渲染函数中没有带有 if 语句的数据,您可以返回其他合适的内容。我更喜欢将过滤器之类的功能移到渲染之外。也许另一种(更好的?)方法是在您的 mapStateToProps 函数中执行该过滤器。

      另外,如果我没记错的话,你不需要向你的 handleChange 函数传递任何东西。因为你是从 CardList 组件中获取 filterText 然后设置你的状态。

      cardList = () => this.props.cards.filter(card => 
                  card.title.includes(this.state.filterText.trim().toLowerCase()));
      
      render() {
       if ( !this.props.cards.length ) {
              return <p>No cards</p>
              // or return <SpinnerComponent />
          }
       return (
                  <div>
                      <CardList cards={this.cardList()}
                          handleChange={this.handleChange} />
                  </div>
              );       
       }
      

      【讨论】:

        猜你喜欢
        • 2020-12-21
        • 1970-01-01
        • 1970-01-01
        • 2018-02-19
        • 2019-02-08
        • 2020-03-09
        • 1970-01-01
        • 2021-07-12
        • 1970-01-01
        相关资源
        最近更新 更多