【问题标题】:React Flux - wait for props before rendering child componentsReact Flux - 在渲染子组件之前等待道具
【发布时间】:2023-04-01 09:30:02
【问题描述】:

快速而“简单”:如何让我的父组件等待某个状态被设置,直到我每次访问父组件(不仅仅是一次)渲染子组件?

更详细一点: 我希望能够根据用户输入过滤结果。我有一个控制器视图“Items”,它调用一个名为“FilterComponent”的子组件并将过滤器状态作为道具传递。我从控制器对动作创建者进行了初始调用,该动作创建者从本地存储中获取保存的过滤器并将它们分派到存储中。根据该结果,我更新过滤器状态并将其作为道具传递给孩子。

我遇到的困难是找到一种方法来确保在设置过滤器状态之前不渲染“FilterComponent”。我可以让它工作,所以当我刷新页面时,它会加载过滤器 - 但是当我在其他地方访问页面并导航到 Items 控制器时,它就会卡住。

我认为这与我通过 action/store 获取过滤器的方式有关,它在 componentDidMount() 中调用 - 因为 componentDidMount() 只调用一次,我需要每次调用它时间。

import React, { PropTypes } from 'react';
import FilterAction from '../../Actions/FilterActions';
import BaseComponent from '../../Components/Base';
import { Form, FormGroup, FormControl, ControlLabel, InputGroup, DropdownButton, MenuItem, Button } from 'react-bootstrap';
import Select from 'react-select';
import _ from 'lodash';

class FilterComponent extends BaseComponent {
    constructor(props) {
        super(props);

        this.state = {};

        this._bind(
            'handleExperienceChange',
            'handlePriceChange',
            'applyFilter'
        );
    }

    componentDidMount() {
        this.applyFilter();
    }

    handlePriceChange(price) {
        var filter = {};
        var product = this.props.product.tag;

        filter[product] = {
            price: price ? price.value : null
        };

        let updatedState = _.merge(this.state, {
            selectedPrice: price || null
        });

        this.updateFilter(filter);

        this.setState(updatedState);
    }

    getPrice() {
        var price = this.props.lang.select.prices;
        var priceSelect = [];

        Object.keys(price).map(function (key) {
            priceSelect.push({
                value: key,
                label: price[key]
            });
        });

        return priceSelect;
    }

    applyFilter() {
        var filters = this.props.filters || null;
        var product = this.props.product || null;

        if (filters && product && filters[product.tag]) {
            var selectedPrice = filter.price ? {
                value: filter.price,
                label: this.props.lang.select.prices[filter.price]
            } : false;
        }

        var updatedState = {
            selectedPrice: selectedPrice || false
        };
        this.setState(_.merge(this.state, updatedState));
    }

    updateFilter(filter) {
        FilterAction.update(filter);
    }

    render() {
        const { product, lang } = this.props;

        return (
            <div className="filter">
                <div className="flex">
                    <div className="pane-50">
                        <Select  name="price"
                                 placeholder={lang.general.form.input.price}
                                 value={this.state.selectedPrice}
                                 options={this.getPrice()}
                                 onChange={this.handlePriceChange}
                                 searchable={false} />
                    </div>
                </div>
            </div>
        );
    }
}

export default FilterComponent;

【问题讨论】:

  • 你能不能只在Items渲染方法中检查这个状态的存在,并且如果设置了状态,只将FilterComponent作为一个孩子包含进来?喜欢(在 jsx 中){this.state.filters ? &lt;FilterComponent {...this.state} /&gt; : 'Loading...'}
  • 感谢您的回复比尔!当我在该特定页面上进行浏览器刷新时它可以工作,但是当我在其他地方进入页面并导航到项目页面时,它在“正在加载...”上没有错误。也许我的子组件是问题所在?我已经更新了我的答案并包含了我的子组件的代码。我还注意到,当我点击刷新时,它在父级中调用 render() 3 次,第三个设置了过滤器,但是当我从另一个页面导航到它时,它只渲染两次,两次过滤器未定义。
  • 您的代码似乎在混合和匹配this.props.filtersthis.state.filters,这对我来说没有意义。在updateFilters 中,您设置了this.state.filters,但您从未在任何地方实际使用过该状态;你在applyFilter 中使用this.props.filters。这可能是你的问题吗?
  • 抱歉让比尔感到困惑,这个例子是我的子组件,它只依赖于this.props.filters(见applyFilters())。 updateFilters 中的过滤器状态不再使用,因为我现在将任何更新的过滤器直接保存到存储中,所以请忽略该位(我已将其从上面的代码中删除)。我现在已经完全从父级中删除了 FilterComponent,它仍然停留在“正在加载...”上。我认为ChangeListener 仅适用于整页重新加载。

标签: reactjs flux


【解决方案1】:

您是否遇到任何错误?变量 filter 在您的 applyFilter() 中似乎未定义。

此外,虽然不相关,但您可以通过使用新键和值调用 setState() 来设置状态,而不必创建整个新状态:

代替:

var updatedState = {
    selectedPrice: selectedPrice || false
};
this.setState(_.merge(this.state, updatedState));

你可以这样做:

this.setState({
    selectedPrice: selectedPrice || false
});

最后,考虑从componentWillMount() 调用applyFilter(),因为它会影响组件的呈现方式。 componentWillMount()render() 之前被调用。

要获得更快的帮助,请考虑在 codepen 或类似服务上为您的项目创建一个简化、独立但有效的示例。

【讨论】:

  • 感谢您的回复 squall3d!我似乎已经弄清楚问题所在了。我在ChangeListener 之前调用了我的动作创建者来获取现有过滤器,这导致它无法监听来自商店的更改。我还必须在 myFilterComponent 中使用 componentDidUpdate(),以确保它在我切换产品时重新呈现,而不仅仅是在整个页面重新加载时。关于您的建议从render() 调用applyFilter - 我认为在render() 中调用任何改变状态的东西是不好的做法?
  • 啊,对了,我的手机格式很差,误读了。我会删除那些废话。
【解决方案2】:

如果您的状态尚未准备好,只需从您的 render() 方法返回 null。每当 state 或 props 更新时,render() 将再次运行。一旦您的状态准备就绪,就可以正常返回您的子组件。

render() {
    const { product, lang } = this.props;

    if (!this.state.selectedPrice)
      return null;

    return (
        <div className="filter">
            <div className="flex">
                <div className="pane-50">
                    <Select  name="price"
                             placeholder={lang.general.form.input.price}
                             value={this.state.selectedPrice}
                             options={this.getPrice()}
                             onChange={this.handlePriceChange}
                             searchable={false} />
                </div>
            </div>
        </div>
    );
}

【讨论】:

    猜你喜欢
    • 2022-12-03
    • 2016-09-15
    • 2019-07-21
    • 1970-01-01
    • 2016-08-17
    • 2021-11-21
    • 2020-08-19
    • 2020-12-12
    • 2022-12-14
    相关资源
    最近更新 更多