这可能是一本很长的书,我已经阅读并尝试了许多解决方案,但没有成功!实际上,我拥有的是三个MySQL表,一个包含一个用户列表,另一个包含一个文件数据列表。它们与第三个表配对,该表有一个用于用户ID的列和一个用于文件ID的列。
当用户登录到应用程序时,它将从表1中获取其ID,然后转到表3,找到与用户ID在同一行中的所有文件ID,然后从表2中返回文件信息。除了不是。
我当前的代码:
componentWillMount() {
this.getClientFiles();
}
哪个电话:
getClientFiles() {
let id = this.props.currentUser.user_id;
let file_refs = [];
axios.get(`/users/get-client-files/${id}`)
.then(res => {
let response = res.data.response;
for (let i = 0; i < response.length; i++) {
file_refs.push(response[i].file_id);
}
this.setState({
file_refs
});
this.getFileData();
});
}
我对此的理解是,{x1}仅应在axios GET请求成功后运行(因为this.getFileData();
)。返回所有文件引用,并将它们添加到数组中,并在客户端会话期间保持状态。
然后这应该运行:
.then
此处,该函数在状态中的fileRefs中循环,调用每个引用ID,然后将其返回到fileData并将其保存到状态。
问题...。登录后第一页加载,文件不呈现。如果您按cmd + R进行刷新,请在此处进行繁荣。我了解承诺链和JS函数的异步特性,我了解getFileData() {
let fileRefs = this.state.file_refs;
let fileData = [];
for (let i = 0; i < fileRefs.length; i++) {
axios
.get("/files/get-file/" + fileRefs[i])
.then(res => {
fileData.push(res.data.response);
this.setState({
client_files: fileData,
returned_data: true
});
})
.catch(err => console.log(err.response.data));
}
}
应该在组件安装之前运行,而componentWillMount()
应该触发组件的重新渲染。
我尝试过的事情:
1)在setState
之前render()
之后添加以下代码:
return(
结果是闪烁的渲染,其中有4-5个渲染,因为函数在returned_data的状态设置为true之前会异步运行。
2)将 if (this.state.returned_data === false) {
this.getClientFiles();
}
移到setState({ returned_data: true })
函数中。这只是提早结束渲染,直到刷新页面之前没有文件。
3)将getClientFiles()
换成componentWillMount()
。
很明显,我缺少的功能链和React的内置方法都有一个基本方面。
有人可以帮忙吗?
编辑#1
问题似乎在于,在第一次渲染时,componentDidMount()
是let id = this.props.currentUser.user_id;
,因此undefined
中的调用实际上将转到getClientFiles
编辑#2-@devserkan请求 我希望这就是你想要的:)
首次加载
/users/get-client-files/undefined
:返回一个空数组
get-client-files/${id}
:不运行
第二个负载:
/get-file/" + fileRefs[i]
:返回包含5个项目的数组
get-client-files/${id}
:根据每个文件的详细信息运行5次。
很明显,问题在于/get-file/" + fileRefs[i]
没得到任何东西,因为它没有get-client-files/${id}
可供搜索。该ID是通过props传递的,但似乎无法立即使用。
编辑#3 这是获取ID并将其设置为状态的函数。
${id}
App.js呈现以下内容:
getUser = () => {
let localToken = localStorage.getItem("iod_tkn");
axios({
url: "/admins/current",
method: "get",
headers: {
Authorization: localToken
}
})
.then(result => {
this.setState({
isLoggedIn: true,
user: result.data,
user_id: result.data.user_id
});
})
.catch(err => {
this.setState({ isLoggedIn: false });
console.log(err);
});
};
因此,通过将render() {
const { loading } = this.state;
if (loading) {
return <Spinner />;
}
return (
<AdminProvider>
<FileProvider>
<Provider>
<Appbar isLoggedIn={this.state.isLoggedIn} logout={this.logout} />
<Main
getUser={this.getUser}
isLoggedIn={this.state.isLoggedIn}
currentUser={this.state.user}
/>
<BottomNav />
</Provider>
</FileProvider>
</AdminProvider>
);
}
传递到Main.js中,一旦收到道具,该组件应该重新渲染,对吗?
答案 0 :(得分:1)
我认为您的代码结构存在问题。 setState
是一个异步函数,它将回调作为第二个参数。您应该利用它的优势。您可以在setState完成后执行一个函数,并使用第二个param回调(updater函数)利用更新后的状态,例如:
this.setState({
file_refs
}, () => {
this.getFileData();
});
已编辑第二个选项,除非您在渲染方法中使用它,否则不应该设置State file_refs
。
试试这个:
axios.get(`/users/get-client-files/${id}`)
.then(res => {
let response = res.data.response;
for (let i = 0; i < response.length; i++) {
file_refs.push(response[i].file_id);
}
this.getFileData(file_refs);
});
getFileData(file_refs) {
let fileRefs = file_refs;
let fileData = [];
// rest of your code
}
让我知道问题是否仍然存在。很高兴提供帮助
答案 1 :(得分:1)
问题在于,在componentWillMount()
中,异步调用可能无法在装入阶段的呈现发生之前按时检索结果,因此您会产生意外的副作用。组件很可能将使用空数据进行渲染。
通过异步调用呈现数据的最佳位置是componentDidMount()
。
请注意,从16.3版本开始,componentWillMount()
被认为是生命周期的不安全方法,在以后的版本中将被删除,因此最好不要再使用它。
答案 2 :(得分:1)
由于您的user_id
来自异步作业,因此您应该执行条件渲染。喜欢:
{ user_id && <ClientDashboard user_id={user_id} ... /> }
此外,您可能还需要整理一些代码:)在这里,我模仿您的应用程序。
const userFiles = [
{ file_id: 1, client_name: "foo" },
{ file_id: 2, client_name: "bar" },
{ file_id: 3, client_name: "baz" },
];
const files = [
{ file_id: 1, name: "fizz", size: 10 },
{ file_id: 2, name: "buzz", size: 20 },
{ file_id: 3, name: "fuzz", size: 30 },
];
const fakeRequest = () => new Promise( resolve =>
setTimeout( () => resolve(userFiles), 1000)
);
const fakeRequest2 = id => new Promise(resolve => {
const file = files.find( el => id === el.file_id );
setTimeout(() => resolve(file), 1000)
}
);
class App extends React.Component {
state = {
file_refs: [],
client_files: [],
returned_data: false,
}
componentDidMount() {
this.getClientFiles();
}
getClientFiles() {
fakeRequest()
.then(res => {
const file_refs = res.map( el => el.file_id );
this.setState({
file_refs
});
this.getFileData();
});
}
getFileData() {
const {file_refs: fileRefs} = this.state;
const promiseArray = fileRefs.map( id => fakeRequest2( id ) );
Promise.all( promiseArray )
.then( results => this.setState({
client_files: results,
returned_data: true,
}))
}
render() {
const { file_refs, client_files } = this.state;
return (
<div>
{!!file_refs.length && <p>File_refs: {JSON.stringify(file_refs)}</p>}
{!!client_files.length && <p>Client files: {JSON.stringify(client_files)}</p>}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
我不喜欢for循环:)