简体   繁体   English

React组件未使用地图功能渲染

[英]React component not getting rendered with map function

My LinkItem component doesn't get rendered when loadAll() is called. 调用loadAll()时,不会渲染我的LinkItem组件。 It is only rendered after handleClick() or handleChange() is called. 仅在调用handleClick()或handleChange()之后才呈现它。

import React, { Component } from "react";
var Datastore = require("nedb");
var db = new Datastore({ filename: __dirname + "/userdata" });
db.loadDatabase(function(err) {

});

function LinkItem(props) {
  console.log("Rendered");
  return (
    <div>
      <a href={props.name}>{props.name}</a>
    </div>
  );
}

export default class List extends Component {
  constructor(props) {
    super(props);
    this.state = {
      link: "",
      data: [],
    };
    this.handleClick = this.handleClick.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.loadAll = this.loadAll.bind(this);
  }

  loadAll() {
    let loadData = [];
    db.find({ type: "link" }, function(err, docs) {
      docs.map(doc => loadData.push(doc.name));
    });
    this.setState({ data: loadData });
  }

  // add new item to data set
  handleClick() {
    this.setState({
      data: [...this.state.data, this.state.link]
    });
    const doc = { name: this.state.link, type: "link" };
    db.insert(doc, (err, docs) => {
      console.log("Inserted");
      console.log(docs);
    });
  }

  // handles form input changes
  handleChange(event) {
    this.setState({ link: event.target.value });
  }

Here in the render function, I am using map to render each LinkItem component. 在渲染功能中,我使用地图来渲染每个LinkItem组件。 However, they aren't being created at all when I check console.log at my LinkItem component. 但是,当我在LinkItem组件上检查console.log时,根本不会创建它们。 They only show up once handleClick, and handleChange is called. 它们仅在handleClick和调用handleChange时出现。

  render() {
    const data = this.state.data;

      return (
        <div>
          <button onClick={this.loadAll}>Load data</button>
          <input
            type="text"
            value={this.state.link}
            onChange={this.handleChange}
          />
          <button onClick={this.handleClick}>Add</button>
          {console.log(data)}
          {/* render each data */}
          {data.map((item, index) => (
            <LinkItem name={item} key={index} />
          ))}
        </div>
      );
    }

}

Basically what I want to happen is when loadData is called, all the data is rendered in LinkItem components. 基本上,我想发生的是调用loadData时,所有数据都在LinkItem组件中呈现。

Note: this isn't a duplicate. 注意:这不是重复项。 I checked other questions in stackoverflow where they didn't have a return in the map function. 我检查了stackoverflow中的其他问题,这些问题在map函数中没有返回。 That's not the case for me. 对我来说不是这样。 It does end up rendered only when other state values change. 仅当其他状态值更改时,它才最终呈现。

EDIT: I was able to fix it by putting setState inside db find and changing the callback to an arrow function. 编辑:我能够通过将setState放在db find中并将回调更改为箭头函数来修复它。 For those of you using nedb with react this might help. 对于使用nedb进行响应的您可能会有所帮助。

  loadAll() {
    db.find({ type: "link" }, (err, docs) => { // changed to arrow function
      this.setState({ data: docs.map(doc => doc.name)});
    });
  } 

Your db.find function is asynchronous, your program will keep on going and not wait for it to finish : 您的db.find函数是异步的,您的程序将继续运行,而不必等待它完成:

  loadAll() {
    let loadData = [];
    db.find({ type: "link" }, function(err, docs) {
      docs.map(doc => loadData.push(doc.name));
    });
    this.setState({ data: loadData });
  }

You have 2 solution to fix it. 您有2个解决方案来修复它。

Putting your setState inside the callback : setState放入回调中:

  loadAll() {
    let loadData = [];
    db.find({ type: "link" }, function(err, docs) {
      docs.map(doc => loadData.push(doc.name));
      this.setState({ data: loadData });
    });
  }

Or awaiting your call : 或等待您的来电:

  loadAll = async() => {
    let loadData = [];
    const docs = await db.find({ type: "link" });
    docs.map(doc => loadData.push(doc.name));
    this.setState({ data: loadData });
  }

Also, the map function will return a new array with your values. 同样,map函数将返回一个包含您的值的新数组。 The following syntax is shorter and does the same thing : 以下语法较短,并且具有相同的作用:

loadAll = async () => {
    this.setState({ 
        data: (await db.find({ type: "link" })).map(doc => doc.name)
    });
}

I also noticed you were using your state values inside setState . 我还注意到您在setState中使用状态值。 React recommends using your previous state in a callback to do so and avoid unexpected behavior : React建议在回调中使用您之前的状态来避免意外行为:

handleClick() {
    this.setState(prevState => {
        data: [...prevState.data, prevState.link]
    });

Another thing you could use is deconstruction, this code : 您可以使用的另一种方法是解构,此代码:

const data = this.state.data;

Could become : 可能成为:

const { data } = this.state;

You need to call to setState inside your callback, after the documents have been retrieved: 检索文档 ,您需要在回调中调用setState

Code

loadAll() {
  let loadData = [];
  db.find({ type: "link" }, function(err, docs) {
    docs.map(doc => loadData.push(doc.name));
    this.setState({ data: loadData });
  });
} 

Or just do: 或者只是做:

loadAll() {
  db.find({ type: "link" }, function(err, docs) {
    this.setState({ data: docs.map(doc => doc.name)});
  });
} 

The reason you have to do that is because db.find is an asynchronous function so, it will return inmediatly so loadData will be equal to [] . 这样做的原因是因为db.find是一个异步函数 ,所以它将立即返回,因此loadData将等于[]

You could also declare loadAll as async and use await : 您还可以将loadAll声明为async并使用await

loadAll() {
  const docs = await db.find({ type: "link" });
  this.setState({ data: docs.map(doc => doc.name) });
}

By the way, there are a few tips that will make your code shorter: 顺便说一下,有一些技巧可以使您的代码更短:

  1. Use the object destructuring syntax 使用对象分解语法
  2. Declare your functions as arrow functions , this way you can get rid of the constructor if you create the state directly inside the class 将函数声明为箭头函数 ,这样,如果您直接在类内部创建state则可以摆脱构造函数

The final code will be something like this: 最终代码将如下所示:

Final code 最终代码

import React, { Component } from "react";

var Datastore = require("nedb");
var db = new Datastore({ filename: __dirname + "/userdata" });
db.loadDatabase(function(err) {

});

function LinkItem(props) {
    console.log("Rendered");
    return (
        <div>
            <a href={props.name}>{props.name}</a>
        </div>
    );
}

export default class List extends Component {
    state = {
        link: "",
        data: [],
    }

    loadAll = async () => {
        const docs = await db.find({ type: "link" });
        this.setState({ data: docs.map(doc => doc.name) });
    }

    // add new item to data set
    handleClick = () => {
        const { data, link } = this.state;

        this.setState({
            data: [...data, link]
        });

        const doc = { name: this.state.link, type: "link" };

        db.insert(doc, (err, docs) => {
            console.log("Inserted");
            console.log(docs);
        });
    }

    // handles form input changes
    handleChange = (event) => {
        this.setState({ link: event.target.value });
    }

    render() {
        const { data } = this.state;

        return (
            <div>
                <button onClick={this.loadAll}>Load data</button>
                    <input
                    type="text"
                    value={this.state.link}
                    onChange={this.handleChange}
                    />
                <button onClick={this.handleClick}>Add</button>

                {console.log(data)}
                {/* render each data */}

                {data.map((item, index) => (
                    <LinkItem name={item} key={index} />
                ))}
            </div>
        );
    }
}

Note that render does not need to be declared as an arrow function because it is automatically bound by React . 请注意,无需将render声明为箭头函数,因为render 由React自动绑定

Let me know if this works, otherwise, tell me what went wrong. 让我知道这是否可行,否则,请告诉我出了什么问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM