【问题标题】:React Router does not update component if url parameter changes如果 url 参数更改,React Router 不会更新组件
【发布时间】:2020-07-10 14:23:00
【问题描述】:

我刚刚在我的网站中实现了全局搜索,但我开始遇到 React-Router 问题。如果 url 更改参数,则不会更新视图。

例如,从/users/454545 导航到/teams/555555 可以正常工作。但是,从 /teams/111111 导航到 teams/222222 会更改 url,但组件仍然是 /teams/111111

这是我的搜索输入字段代码。

const SearchResult = ({ id, url, selectResult, text, type }) => (
    <Row key={id} onClick={() => selectResult(url)} width='100%' padding='5px 15px 5px 15px' style={{cursor: 'pointer'}}>
        <Column  alignItems='flex-start' style={{width: '100%'}}>
            <Label textAlign='left' color='#ffffff'>{text}</Label>
        </Column>
        <Column style={{width: '100%'}}>
            <Label textAlign='right' color='#ffffff'>{type}</Label>
        </Column>
    </Row>
)

const SearchInput = (props) => {
    const { isSearching, name, onChange, onClear, results } = props;

    return (
        <Section width='100%' style={{display: 'flex', position: 'relative'}}>
            <Wrapper height={props.height} margin={props.margin}>
                <i className="fas fa-search" style={{color: 'white'}} />
                <input id='search_input' placeholder={'Search for a team, circuit, or user'} name={name} onChange={onChange} style={{outline: 'none', backgroundColor: 'transparent', borderColor: 'transparent', color: '#ffffff', width: '100%'}} />
                {onClear && !isSearching && <i onClick={onClear} className="fas fa-times-circle" style={{color: '#50E3C2'}} />}
                {isSearching && 
                <Spinner viewBox="0 0 50 50" style={{marginBottom: '0px', height: '50px', width: '50px'}}>
                    <circle
                    className="path"
                    cx="25"
                    cy="25"
                    r="10"
                    fill="none"
                    strokeWidth="4"
                     />
                </Spinner>
                }
            </Wrapper>
            {results && <Section backgroundColor='#00121A' border='1px solid #004464' style={{maxHeight: '400px', position: 'absolute', top: '100%', left: '0px', width: '97%', overflowY: 'scroll'}}>
                <Section backgroundColor='#00121A' style={{display: 'flex', flexDirection: 'column', padding: '15px 0px 0px 0px', justifyContent: 'center', alignItems: 'center', width: '100%'}}>
                    {results.length === 0 && <Text padding='0px 0px 15px 0px' color='#ffffff' fontSize='16px'>We didn't find anything...</Text>}
                    {results.length !== 0 && results.map(r => <SearchResult selectResult={props.selectResult} id={r._id} url={r.url} text={r.text} type={r.type} />)}
                </Section>
            </Section>}
        </Section>
    )
}

export default SearchInput;

父组件是一个导航栏,看起来像这样。为了便于阅读,我对其进行了精简。

import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';

import SearchInput from '../shared/inputs/SearchInput';

const TopNav = (props) => {
    const [search, setSearch] = useState(null);
    const [searchResults, setSearchResults] = useState(null);
    const debouncedSearchTerm = useDebounce(search, 300);
    const [isSearching, setIsSearching] = useState(false);

    function clearSearch() {
        document.getElementById('search_input').value = '';
        setSearchResults(null);
    }

    function searchChange(e) {
        if (!e.target.value) return setSearchResults(null);
        setSearch(e.target.value);
        setIsSearching(true);
    }

    async function updateQuery(query) {
        const data = {
            search: query
        }
        
        const results = await api.search.query(data);

        setSearchResults(results);
        setIsSearching(false);
    }

    function selectResult(url) {
        props.history.push(url);
        setSearchResults(null);
    }

    function useDebounce(value, delay) {
        // State and setters for debounced value
        const [debouncedValue, setDebouncedValue] = useState(value);
      
        useEffect(
          () => {
            // Update debounced value after delay
            const handler = setTimeout(() => {
              setDebouncedValue(value);
            }, delay);
      
            // Cancel the timeout if value changes (also on delay change or unmount)
            // This is how we prevent debounced value from updating if value is changed ...
            // .. within the delay period. Timeout gets cleared and restarted.
            return () => {
              clearTimeout(handler);
            };
          },
          [value, delay] // Only re-call effect if value or delay changes
        );
      
        return debouncedValue;
      }

    useEffect(() => {

        if (debouncedSearchTerm) {
            
            updateQuery(debouncedSearchTerm);
          } else {
            setSearchResults(null);
          }
    }, [user, debouncedSearchTerm])

    return (
        <ContentContainer style={{boxShadow: '0 0px 0px 0 #000000', position: 'fixed', zIndex: 1000}}  backgroundColor='#00121A' borderRadius='0px' width='100%'>
            <Section style={{display: 'flex', justifyContent: 'center', alignItems: 'center', height: '50px'}} width='1200px'>
                <SearchInput height={'30px'} margin='0px 20px 0px 0px' isSearching={isSearching} selectResult={selectResult} onChange={searchChange} onClear={clearSearch} results={searchResults} />
            </Section>
        </ContentContainer>
    )
}

function mapStateToProps(state) {
    return {
        user: state.user.data,
        notifs: state.notifs
    }
}

export default connect(mapStateToProps, { logout, fetchNotifs, updateNotifs })(TopNav);

Tl;DR

使用 react-router 进行站点导航。如果从/teams/111111 导航到/teams/222222,则不会更新组件,但如果从/users/111111 导航到/teams/222222,则会更新。

感谢您的任何帮助!

【问题讨论】:

  • 您的状态是否取决于道具,即在组件中您是否使用搜索 ID(如 111111 或 2222)作为状态?
  • @RohanAgarwal 用户/团队页面的状态取决于道具,是的。实体的 id 通过 componentdidmount / useEffect 拉入,然后填充所有其他数据。这有帮助吗?

标签: javascript reactjs react-router


【解决方案1】:

当一个 URL 的路径改变时,当前组件被卸载,新 URL 指向的新组件被挂载。但是,当 URL 的参数发生变化时,由于新旧 URL 路径指向同一个组件,因此不会发生 unmount-remount;只有已经安装的组件会收到新的道具。人们可以利用这些新道具来获取新数据并渲染更新的 UI。

假设你的参数 id 是parameter

  1. 带钩子:

    useEffect(() => {
        // ... write code to get new data using new prop, also update your state
    }, [props.match.params.parameter]);
    
  2. 使用类组件:

    componentDidUpdate(prevProps){
        if(this.props.match.params.parameter!== prevProps.match.params.parameter){
            // ... write code to get new data using new prop, also update your state
        }
    }
    
  3. 使用密钥:

    另一种方法是使用唯一键道具。传递新密钥将强制 要重新安装的组件。

    <Route path="/teams/:parameter" render={(props) => (
        <Team key={props.match.params.parameter} {...props} />
    )} />
    

【讨论】:

  • 关键方法非常适合我的用例 - 谢谢! :D
【解决方案2】:

重新渲染不会导致组件重新挂载,因此只要 props 更改并在回调中更新您的状态,请使用 useEffect 挂钩调用组件中的初始化逻辑。

useEffect(() => {
  //Re initialize your component with new url parameter
}, [props]);

【讨论】:

  • 好的,我试试看!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-11-23
  • 2017-11-07
  • 2018-12-29
  • 2020-12-17
  • 1970-01-01
  • 1970-01-01
  • 2021-01-19
相关资源
最近更新 更多