简体   繁体   English

如何防止组件在数据获取之前呈现子级

[英]How to prevent component from rendering child until data fetches

I have a Dashboard component that renders an array of cards with data fetched from a backend server. 我有一个Dashboard组件,该组件使用从后端服务器获取的数据来渲染卡阵列。 Users can create additional cards by submitting a form, which then redirects them back to the dashboard page. 用户可以通过提交表单来创建其他卡,然后将其重定向回仪表板页面。

My issue is that when the form is submitted, a javascript error 'cannot read property "includes" of undefined' is thrown and the dashboard does not render. 我的问题是,提交表单后,将引发javascript错误“无法读取未定义的属性”包含”,并且仪表板无法呈现。 If I manually refresh the page, the list renders as expected with the new card. 如果我手动刷新页面,则新卡将按预期呈现该列表。 I use Array.includes method to filter the cards based on the filterText state value. 我使用Array.includes方法基于filterText状态值来过滤卡。 Does this error happen because the data has not been fetched when render is called? 是否由于调用render时未获取数据而发生错误? If so, how can I force the component to wait until there is data before rendering? 如果是这样,如何强制组件等待直到有数据再渲染? Please see the components and redux action below. 请参阅下面的组件和还原操作。

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');
}

Similar to what @JasonWarta mentioned, it's worth noting that React does not render anything when false , null , or undefined is returned, so you can usually use && to be more succinct than using the conditional ("ternary") operator: 与@JasonWarta提到的类似,值得注意的是,当返回falsenullundefined时,React不会呈现任何内容,因此与使用条件(“三元”)运算符相比,您通常可以使用&&更简洁:

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>
  );
}

Because && short-circuits, the latter part won't be evaluated so you can avoid TypeError s, and the component will also render no content (same as when you return null ). 由于&&短路,因此不会评估后一部分,因此可以避免TypeError ,并且该组件也不会呈现任何内容(与返回null相同)。

I've used ternary operators in this kind of situation. 在这种情况下,我使用了三元运算符。 You may need to adjust the check portion of the pattern, depending on what your redux pattern is returning. 您可能需要调整模式的检查部分,具体取决于您的redux模式返回什么。 null value is returned if this.props.cards is falsey. 如果this.props.cards为false,则返回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
        }
    );
}

As an alternative to other answers you can return something else suitable if there is no data in your render function with an if statement. 作为其他答案的替代方法,如果使用if语句的渲染函数中没有数据,则可以返回其他合适的内容。 I prefer moving functions like your filter one outside of render. 我更喜欢将诸如过滤器之类的功能移至渲染之外。 Maybe one other (better?) approach is doing that filter in your mapStateToProps function. 也许另一种(更好的方法)是在mapStateToProps函数中执行该过滤器。

Also, if I'm not wrong you don't need to pass anything to your handleChange function. 另外,如果我没记错的话,您也不需要将任何内容传递给handleChange函数。 Because you are getting filterText back from CardList component then setting your state. 因为要从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>
        );       
 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM