确实需要一些帮助,在渲染之前弄清楚componentWillMount()的逻辑

时间:2018-08-23 21:28:29

标签: javascript reactjs axios

这可能是一本很长的书,我已经阅读并尝试了许多解决方案,但没有成功!实际上,我拥有的是三个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中,一旦收到道具,该组件应该重新渲染,对吗?

3 个答案:

答案 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循环:)