反应使数据保持同步

时间:2019-05-06 13:14:20

标签: javascript reactjs

我在页面上有一个实时过滤器结构。来自输入的数据既保留在我的州,也保留为URL查询,以便当有人打开带有URL中的过滤器的页面时,已经选择了正确的过滤器。

我正在努力寻找一种稳定的方法来使这2个保持同步。目前,我是在加载时从URL中获取数据,并在更改状态时在URL中设置数据,但是这种结构实际上使得无法重用所涉及的组件,并且错误很容易导致无限循环,这实际上也是无法扩展。有没有更好的架构来处理这些同步?

2 个答案:

答案 0 :(得分:2)

我建议从查询参数管理视图中过滤器的状态。如果使用react-router,则可以使用查询参数代替状态,并且在render方法中获取视图元素所需的参数。更改过滤器后,您需要实现重定向。为了更加方便,最好使用qs module。使用这种方法,您还将收到一个现成的参数,用于请求后端。

示例容器:

const initRequestFields = {someFilterByDefault: ''};

class Example extends Component{
  constructor(props) {
    super(props);

    this.lastSearch = '';
  }

  componentDidMount() {
    this.checkQuery();
  }

  componentDidUpdate() {
    this.checkQuery();
  }

  checkQuery() {
    const {location: {search}, history} = this.props;

    if (search) {
      this.getData();
    } else {
      history.replace({path: '/some-route', search: qs.stringify(initRequestFields)});
    }
  }

  getData() {
    const {actionGetData, location: {search}} = this.props;
    const queryString = search || `?${qs.stringify(initRequestFields)}`;
    if (this.lastSearch !== queryString) {
      this.lastSearch = queryString;
      actionGetData(queryString);
    }
  }

  onChangeFilters = (values) => {
    const {history} = this.props;

    history.push({path: '/some-route', search: qs.stringify(values)});
  };

  render() {
    const {location: {search}} = this.props;

    render(<Filters values={qs.parse(search)} onChangeFilers={this.onChangeFilters} />)
  }
}

此逻辑最好保存在将值传递给组件的最高容器中。

有关更多信息,请访问

Query parameters in react router

Qs module for ease work with query

If you worry about bundle size with qs module

答案 1 :(得分:0)

此答案使用了反应钩子


您想使URLstate保持同步,需要从URLstate(组件安装时)和{ stateURL(更新过滤器时)。

使用反应路由器挂钩,您可以使用URL获取反应性对象,并将其用作state,这是一种方法-从URL到组件。

相反的方法-更改组件后更新URL,可以使用history.replace

您可以在自定义钩子中隐藏这两种方式,它的工作方式类似于常规的useState钩子:

要将查询参数用作状态:

import { useHistory, useLocation} from 'react-router-dom'

const useQueryAsState = () => {
    const { pathname, search } = useLocation()
    const history = useHistory()

    // helper method to create an object from URLSearchParams
    const params = getQueryParamsAsObject(search)

    const updateQuery = (updatedParams) => {
        Object.assign(params, updatedParams)
        // helper method to convert {key1:value,k:v} to '?key1=value&k=v'
        history.replace(pathname + objectToQueryParams(params))
    }

    return [params, updateQuery]
}

要将路由参数用作状态:

import { generatePath, useHistory, useRouteMatch } from 'react-router-dom'

const useParamsAsState = () => {
    const { path, params } = useRouteMatch()
    const history = useHistory()

    const updateParams = (updatedParams) => {
        Object.assign(params, updatedParams)
        history.push(generatePath(path, params))
    }
    return [params, updateParams]
}

请注意查询参数代码中的history.replace路由参数代码中的history.push

用法:(不是我的代码中的真正组件,对不起,如果有编译问题)

const ExampleComponent = () => {
    const [{ user }, updateParams] = useParamsAsState()
    const [{ showDetails }, updateQuery] = useQueryAsState()

    return <div>
        {user}<br/ >{showDetails === 'true' && 'Some details'}
        <DropDown ... onSelect={(selected) => updateParams({ user: selected }) />
        <Checkbox ... onChange={(isChecked) => updateQuery({ showDetails: isChecked} })} />
    </div>
}

我将此自定义钩子发布为 npm包use-route-as-state