reactjs - React Component 不会在使用 useContext 的上下文更改时重新渲染
问题描述
我正在尝试在侧边栏组件中处理的上下文值更改( )上重新呈现表格组件currentRep
,我正在使用useContext
. When the selection is changed, the state at the App
-level changes, which is passed through the context provider. 此上下文在表组件中使用并触发useEffect
挂钩以获取数据。虽然触发了这个钩子,但表格不会重新渲染。我曾在某处读到该useContext
钩子每次都会触发值更改的渲染,所以我对(缺乏)结果感到困惑。
我可以确认在选择更改useEffect
时表中的钩子会触发并且我会收到 API 响应。我错过了什么导致重新渲染?我尝试将 设置setCurrentRep
为 -level 的状态设置器App
,但这并没有改变任何东西。
这是创建的上下文:
上下文.tsx
import React from 'react'
import { AccountRep } from './Data';
export interface InfoContextInterface {
repData:AccountRep[];
currentRep: string;
setCurrentRep: (r:string) => void;
}
const InfoContext = React.createContext<InfoContextInterface>({
repData: [],
currentRep:"",
setCurrentRep: (rep) => {}
});
export const InfoProvider = InfoContext.Provider
export default InfoContext
上下文提供者是 -level 中的父App
级:
应用程序.tsx
import React, {useState, useRef, useEffect} from 'react'
import Sidebar from './Sidebar'
import {Switch,Route,useLocation} from 'react-router-dom';
import Home from './pages/Home'
import SchoolInfo from './pages/SchoolInfo'
import {InfoProvider} from './Context'
import {Location} from 'history'
import {AccountRep} from './Data'
export default function App() {
const [data, setData] = useState<AccountRep[]>([]);
const [currentRep,setCurrentRep] = useState("")
const cancelRequest = useRef<boolean>(false);
let location = useLocation()
let locState = location.state as locationStateProps
let background = locState && locState.background
useEffect(() => {
const getReps = async () => {
const req = await (await fetch(`http://localhost:8080/api/reps`)).json()
if (cancelRequest.current) {return}
setData(req)
};
void getReps()
return () => {
cancelRequest.current = true
}
}, []);
return (
<>
<InfoProvider value={{repData:data,currentRep:currentRep,setCurrentRep}}>
<Sidebar />
<Switch location={background || location}>
<Route exact path='/' children={<Home/>}/>
<Route path='/school-info' exact children={<SchoolInfo />}/>
</Switch>
</InfoProvider>
</>
);
};
带有选择的侧边栏,其中上下文更改:
侧边栏.tsx
import React, {useState, useCallback,useContext} from 'react'
import './Sidebar.css'
import InfoContext from './Context'
import {AccountRep} from './Data'
const RepViewDropdown = () => {
const [selectState, setState] = useState("");
const context = useContext(InfoContext)
const handleChangeHandler = useCallback((event: React.FormEvent<HTMLSelectElement>): void => {
setState(event.currentTarget.value)
context.setCurrentRep(event.currentTarget.value)
},[]);
const renderOptions = (repData: AccountRep[]) => {
return repData.map(obj => {
return <option key={obj.id} value={obj.id}>{obj.first_name}</option>
})
}
return (
<fieldset>
<legend>Account Rep</legend>
<select value={selectState} id="select-rep" onChange={handleChangeHandler}>
<option value="">All</option>
{renderOptions(context.repData)}
</select>
</fieldset>
)
}
const Sidebar = () => {
return (
<div className="sidebar">
<RepViewDropdown />
</div>
)
}
export default Sidebar;
和有问题的表格组件:
表格.tsx
import { useEffect, useState, useRef, useContext } from 'react';
import { Link, useLocation } from 'react-router-dom';
import InfoContext,{ InfoContextInterface } from '../Context'
import { School } from '../Data'
import CreateSchoolModal from '../CreateSchoolModal';
import './SchoolInfo.css'
interface SchoolInfoTableRowProps {
data: School;
}
const SchoolInfoTableRow = (props:SchoolInfoTableRowProps) => {
const location = useLocation()
return (
<tr>
<td><Link to={{
pathname:`/school/${props.data.id}`,
state: { background: location }
}}>{props.data.name}</Link></td>
<td><Link to={{
pathname:`/school/${props.data.id}`,
state: { background: location }
}}>{props.data.code}</Link></td>
<td><Link to={{
pathname:`/school/${props.data.id}`,
state: { background: location }
}}>{props.data.address}</Link></td>
</tr>
)
}
const SchoolInfoTable = () => {
const cancelRequest = useRef<boolean>(false);
const [data, setData] = useState<School[]>([]);
const context: InfoContextInterface = useContext(InfoContext)
useEffect(() => {
const fetchData = async () => {
const res = await (await fetch(`http://localhost:8080/api/schools${context.currentRep !== "" ? `?account_rep_id=${context.currentRep}` : ""}`)).json()
if (cancelRequest.current) { return }
setData(res)
};
void fetchData();
console.log(context.currentRep)
return () => {
cancelRequest.current = true
}
}, [context.currentRep]);
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Code</th>
<th>Address</th>
</tr>
</thead>
<tbody>
{data.map((obj,i) => {
return ( <SchoolInfoTableRow key={i} data={obj}/> )
})}
</tbody>
</table>
)
}
const SchoolInfo = () => {
const [showModal, setModal] = useState(false)
const openModal = () => {
setModal(prev => !prev)
}
return (
<div id='school-info'>
<h1 className='page-header'>School Info</h1>
<div className="table-buttonset">
<div className="buttonset">
<button onClick={openModal}>Add School</button>
<button disabled>Test Button</button>
<button disabled>Test Button</button>
</div>
<SchoolInfoTable/>
<CreateSchoolModal showModal={showModal} setModal={setModal}/>
</div>
</div>
)
}
export default SchoolInfo;
解决方案
我已经找到了解决方案。因为无论如何我最初都在获取所有数据,所以我可以只根据上下文值渲染行,而不是每次都在useEffect
挂钩中获取新数据。
const renderTable = (data:School[]) => {
if (context.currentRep) {
data = data.filter(obj => obj.account_rep_id == context.currentRep)
}
return data.map((obj,i) => {
return ( <SchoolInfoTableRow key={i} data={obj}/> )
})
}
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Code</th>
<th>Address</th>
</tr>
</thead>
<tbody>
{renderTable(data)}
</tbody>
</table>
)
推荐阅读
- python - SQLalchemy 在捕获异常时回滚?
- node.js - Docker 中的 Nodejs + PhantomJS:错误:spawn - ENOENT
- mongodb - 在 MongoDB 的 Go 客户端中,如何注册 Hook Decoder 来处理接口的解组 BSON?
- colors - 如何检查颜色是否为黑色
- javascript - 如果项目具有活动类别,则按顺序显示图像
- python - 我想检查opencv中的模板是否匹配
- javascript - 如何显示用户之前输入的建议输入值
- concatenation - Qlik sense concatenate 和 join 是水平合并表而不是垂直合并表
- javascript - 获取帖子请求有效,但 axios post 无效?
- amazon-web-services - 如何在主要 punity.de 上的 cloudflare 上提供静态前端,同时在 aws 上管理 api.punity.de 和其他服务