Link 的 props 发生变化时如何更新状态?
How to update state when props from Link change?
我正在像这样从 component1 中的智能合约获取数据(这是一个带有链接的菜单:
const [contract, setContract] = useState()
const [projects, setProjects] = useState([])
const getData = async () => {
const provider = new ethers.providers.Web3Provider(window.ethereum)
await provider.send('eth_requestAccounts', [])
const campaignContract = new ethers.Contract(
process.env.REACT_APP_CAMPAIGNS_CONTRACT_ADDRESS,
CampaignsAbi,
provider,
)
setContract(campaignContract)
const numberOfProjects = await contract.numberOfCampaigns()
const campaigns = []
for (let i = 1; i <= numberOfProjects; i++) {
campaigns.push(await contract.campaigns(i))
}
setProjects([...campaigns])
}
useEffect(() => {
getData()
}, [contract])
return (
<div className="App-top-bar">
<div className="App-top-bar-menu">
<Link className="link" to="/projects">
<MenuButton title={'Donate'} />
</Link>
<Link
className="link"
to={{
pathname: '/projects/my-projects',
}}
state={projects}>
<MenuButton title={'My Projects'} />
</Link>
</div>
</div>
)
路径“/projects/my-projects”指向 'MyProjects.js'。
//index.js
ReactDOM.render(
<React.StrictMode>
<MoralisProvider serverUrl={serverURL} appId={applicationID}>
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route path="/projects" element={<Projects />}>
<Route path="/projects/my-projects" element={<MyProjects />} />
</Route>
</Route>
<Route
path="*"
element={
<div>
404 :(
</div>
}
/>
</Routes>
</BrowserRouter>
</MoralisProvider>
</React.StrictMode>,
document.getElementById('root'),
)
这是它的样子:
const MyProjects = () => {
return (
<div>
<ProjectList />
</div>
)
}
const ProjectList = () => {
const location = useLocation()
const { state: projects } = useLocation()
return (
<div className="donate-body">
{projects && projects.length !== 0 ? (
projects.map((campaign) => <Project projectDetails={campaign} />)
) : (
<h1>No Projects</h1>
)}
</div>
)
}
我希望我的 'ProjectList' 在合同中的数据更改和 'projects' 更改时更新。有什么帮助吗?感觉我犯了一个 React 错误。
您可以使用 useEffect
挂钩将状态更新加入队列,以“同步”传递的路由状态与本地组件状态。
示例:
const ProjectList = () => {
const location = useLocation();
const [projects, setProjects] = useState(location.state);
useEffect(() => {
setProjects(location.state);
}, [location.state]);
return (
<div className="donate-body">
{projects.map((campaign) => (
<Project projectDetails={campaign} />
))}
</div>
);
};
请记住,在 React 中通常认为 anti-pattern 将传递的 state/props/etc 存储到本地组件状态。直接消费值是很常见的。
示例:
const ProjectList = () => {
const { state: projects } = useLocation();
return (
<div className="donate-body">
{projects?.map((campaign) => (
<Project projectDetails={campaign} />
))}
</div>
);
};
更新
如果你想要一个单一的事实来源,那么我建议使用 React 上下文来存储 projects
状态。
示例:
import { createContext, useContext, useState } from 'react';
export const ProjectsContext = createContext({
projects: [],
setProjects: () => {},
});
export const useProjects = () => useContext(ProjectsContext);
const ProjectsProvider = ({ children }) => {
const [projects, setProjects] = useState(location.state);
return (
<ProjectsContext.Provider value={{ projects, setProjects }}>
{children}
</ProjectsContext>
);
};
export default ProjectsProvider;
提供上下文。
import ProjectsProvider from '../path/to/ProjectsProvider';
...
ReactDOM.render(
<React.StrictMode>
<ProjectsProvider>
<MoralisProvider serverUrl={serverURL} appId={applicationID}>
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route path="/projects" element={<Projects />} />
<Route path="/projects/my-projects" element={<MyProjects />} />
</Route>
<Route path="*" element={<div>404 :(</div>} />
</Routes>
</BrowserRouter>
</MoralisProvider>
</ProjectsProvider>
</React.StrictMode>,
document.getElementById('root'),
);
项目
import { useProjects } from '../path/to/ProjectsProvider';
...
const { setProjects } = useProjects();
useEffect(() => {
const getData = async () => {
const provider = new ethers.providers.Web3Provider(window.ethereum);
await provider.send('eth_requestAccounts', [])
const campaignContract = new ethers.Contract(
process.env.REACT_APP_CAMPAIGNS_CONTRACT_ADDRESS,
CampaignsAbi,
provider,
);
setContract(campaignContract);
const numberOfProjects = await contract.numberOfCampaigns();
const campaignsPromises = [];
for (let i = 1; i <= numberOfProjects; i++) {
campaigns.push(contract.campaigns(i));
}
const campaigns = await Promise.all(campaignsPromises);
setProjects(campaigns);
};
getData();
}, [contract]);
return (
<div className="App-top-bar">
<div className="App-top-bar-menu">
<Link className="link" to="/projects">
<MenuButton title='Donate' />
</Link>
<Link className="link" to='/projects/my-projects'>
<MenuButton title='My Projects' />
</Link>
</div>
</div>
);
我的项目
import { useProjects } from '../path/to/ProjectsProvider';
...
const MyProjects = () => {
return (
<div>
<ProjectList />
</div>
);
};
const ProjectList = () => {
const { projects } = useProjects();
return (
<div className="donate-body">
{projects.length ? (
projects.map((campaign) => <Project projectDetails={campaign} />)
) : (
<h1>No Projects</h1>
)}
</div>
);
};
我正在像这样从 component1 中的智能合约获取数据(这是一个带有链接的菜单:
const [contract, setContract] = useState()
const [projects, setProjects] = useState([])
const getData = async () => {
const provider = new ethers.providers.Web3Provider(window.ethereum)
await provider.send('eth_requestAccounts', [])
const campaignContract = new ethers.Contract(
process.env.REACT_APP_CAMPAIGNS_CONTRACT_ADDRESS,
CampaignsAbi,
provider,
)
setContract(campaignContract)
const numberOfProjects = await contract.numberOfCampaigns()
const campaigns = []
for (let i = 1; i <= numberOfProjects; i++) {
campaigns.push(await contract.campaigns(i))
}
setProjects([...campaigns])
}
useEffect(() => {
getData()
}, [contract])
return (
<div className="App-top-bar">
<div className="App-top-bar-menu">
<Link className="link" to="/projects">
<MenuButton title={'Donate'} />
</Link>
<Link
className="link"
to={{
pathname: '/projects/my-projects',
}}
state={projects}>
<MenuButton title={'My Projects'} />
</Link>
</div>
</div>
)
路径“/projects/my-projects”指向 'MyProjects.js'。
//index.js
ReactDOM.render(
<React.StrictMode>
<MoralisProvider serverUrl={serverURL} appId={applicationID}>
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route path="/projects" element={<Projects />}>
<Route path="/projects/my-projects" element={<MyProjects />} />
</Route>
</Route>
<Route
path="*"
element={
<div>
404 :(
</div>
}
/>
</Routes>
</BrowserRouter>
</MoralisProvider>
</React.StrictMode>,
document.getElementById('root'),
)
这是它的样子:
const MyProjects = () => {
return (
<div>
<ProjectList />
</div>
)
}
const ProjectList = () => {
const location = useLocation()
const { state: projects } = useLocation()
return (
<div className="donate-body">
{projects && projects.length !== 0 ? (
projects.map((campaign) => <Project projectDetails={campaign} />)
) : (
<h1>No Projects</h1>
)}
</div>
)
}
我希望我的 'ProjectList' 在合同中的数据更改和 'projects' 更改时更新。有什么帮助吗?感觉我犯了一个 React 错误。
您可以使用 useEffect
挂钩将状态更新加入队列,以“同步”传递的路由状态与本地组件状态。
示例:
const ProjectList = () => {
const location = useLocation();
const [projects, setProjects] = useState(location.state);
useEffect(() => {
setProjects(location.state);
}, [location.state]);
return (
<div className="donate-body">
{projects.map((campaign) => (
<Project projectDetails={campaign} />
))}
</div>
);
};
请记住,在 React 中通常认为 anti-pattern 将传递的 state/props/etc 存储到本地组件状态。直接消费值是很常见的。
示例:
const ProjectList = () => {
const { state: projects } = useLocation();
return (
<div className="donate-body">
{projects?.map((campaign) => (
<Project projectDetails={campaign} />
))}
</div>
);
};
更新
如果你想要一个单一的事实来源,那么我建议使用 React 上下文来存储 projects
状态。
示例:
import { createContext, useContext, useState } from 'react';
export const ProjectsContext = createContext({
projects: [],
setProjects: () => {},
});
export const useProjects = () => useContext(ProjectsContext);
const ProjectsProvider = ({ children }) => {
const [projects, setProjects] = useState(location.state);
return (
<ProjectsContext.Provider value={{ projects, setProjects }}>
{children}
</ProjectsContext>
);
};
export default ProjectsProvider;
提供上下文。
import ProjectsProvider from '../path/to/ProjectsProvider';
...
ReactDOM.render(
<React.StrictMode>
<ProjectsProvider>
<MoralisProvider serverUrl={serverURL} appId={applicationID}>
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route path="/projects" element={<Projects />} />
<Route path="/projects/my-projects" element={<MyProjects />} />
</Route>
<Route path="*" element={<div>404 :(</div>} />
</Routes>
</BrowserRouter>
</MoralisProvider>
</ProjectsProvider>
</React.StrictMode>,
document.getElementById('root'),
);
项目
import { useProjects } from '../path/to/ProjectsProvider';
...
const { setProjects } = useProjects();
useEffect(() => {
const getData = async () => {
const provider = new ethers.providers.Web3Provider(window.ethereum);
await provider.send('eth_requestAccounts', [])
const campaignContract = new ethers.Contract(
process.env.REACT_APP_CAMPAIGNS_CONTRACT_ADDRESS,
CampaignsAbi,
provider,
);
setContract(campaignContract);
const numberOfProjects = await contract.numberOfCampaigns();
const campaignsPromises = [];
for (let i = 1; i <= numberOfProjects; i++) {
campaigns.push(contract.campaigns(i));
}
const campaigns = await Promise.all(campaignsPromises);
setProjects(campaigns);
};
getData();
}, [contract]);
return (
<div className="App-top-bar">
<div className="App-top-bar-menu">
<Link className="link" to="/projects">
<MenuButton title='Donate' />
</Link>
<Link className="link" to='/projects/my-projects'>
<MenuButton title='My Projects' />
</Link>
</div>
</div>
);
我的项目
import { useProjects } from '../path/to/ProjectsProvider';
...
const MyProjects = () => {
return (
<div>
<ProjectList />
</div>
);
};
const ProjectList = () => {
const { projects } = useProjects();
return (
<div className="donate-body">
{projects.length ? (
projects.map((campaign) => <Project projectDetails={campaign} />)
) : (
<h1>No Projects</h1>
)}
</div>
);
};