javascript - react .js : Parent state values not passing into child properties + Fetch API data cannot be accessed

标签 javascript reactjs fetch-api

我在开发的一个非常基本的颜色和谐选择器中遇到了几个问题。我仍然是 React 和 JSX 的初学者。我最初把它放在 GitHub 上,所以完整的文件在那里,但我把它移到了 Codepen。

Here is the Codepen

我做了很多评论,如果他们有点多,我很抱歉,但希望他们有所帮助。我的问题直到第 41 行,DataStore 类的 displayHarmonies() 方法才开始。传递给它的值来自我的 App(父)组件:

displayHarmonies(color, harmony) {
    //color and harmony pass in dynamically just fine...this.data will not return anything, not even "undefined"
    console.log(color + " is the color and " + harmony + " is the harmony...and dataStore.displayHarmonies says: " + this.data);

    this.registeredWatchers.map((watcher) => {
        let result = "not green"; //result and resultHex will be determined with an underscore statement that will associate the color & harmony choice (primary + foreign key concept) and will return correct harmony color(s)
        let resultHex = "#HEX";

        appState.harmonyColor = result;
        appState.harmonyHex = resultHex;

        //call to app component's onDataChange() method, where new states will be set using the the appState data we just set in lines 49 and 50
        watcher.onDataChange();
    })
}  

从我的第一条评论中可以看出,唯一没有登录到控制台的部分是 this.data,它是在 DataStore 的构造函数中设置的:
constructor(data) {
    //store that data in the object
    //data is not being received from object instance of dataStore on line 187
    this.data = data;

在第 187 行,我创建了一个 DataStore 的实例并将一个名为 data 的变量传递给它。 .在使用之前,这个变量被初始化,然后通过 Fetch API 分配给解析的 JSON 数据:
let data = [];

//use polyfill for older browsers to do Ajax request
fetch("data/data.json").then((response) => {
//if we actually got something
    if (response.ok) {
        //then return the text we loaded
        return response.text();
    }
}).then((textResponse) => {
    data = JSON.parse(textResponse);
});

如果我在第二次获取 .then() 中调出数据方法,JSON 回来就好了。一旦我尝试使用 data变量在应用程序的任何其他地方,它什么都不返回,如 displayHarmonies() 所示方法console.log() .所以这是我的第一个问题,但在我想解决这个问题之前,我想解决我遇到的另一个问题。

appState 之后对象(在 DataStore 之前初始化,在 fetch 语句下)值设置为 result变量,displayHarmonies()运行 watcher.onDataChange() (在 App 组件/父级中)其中 harmonyColorharmonyHex状态被分配给新的 appState值(value)观:
onDataChange() {
    console.log("onDataChange() in App called");
    this.setState({
        harmonyColor: appState.harmonyColor,
        harmonyHex: appState.harmonyHex
    })
}

如果我将这些状态记录到控制台,它们就是正确的值,所以这不是问题。然后我将我的状态传递给 Display要用作属性的子组件:
<Display colorChoice={this.state.currentColor} harmonyChoice={this.state.currentHarmony} harmonyColor={this.state.harmonyColor} harmonyHex={this.state.harmonyHex} /> 

然后我设置Display构造函数中的组件状态,将它们分配给随着应用程序的每次新呈现发送给它的 Prop 。然后我使用 Display 将数据显示到 DOM 上。组件的渲染方法。奇怪的是,应用程序会很好地显示初始状态(颜色:红色、和谐:直接、和谐颜色:绿色等),但是一旦进行更改,DOM 上的数据就不会更新。初始数据以相同的方式加载:通过将父级的状态传递给子级的属性。我有几个console.log() s 似乎证明了为什么这应该有效,但是,它没有。那么我做错了什么?

谢谢,希望这不是一个问题!

最佳答案

首先是您当前的代码,在帖子的末尾,我添加了一个替代解决方案,所以如果这是 tl;dr;只需跳到最后的片段:)

首先要说的是 data 变量,您希望将其传递给您的 DataStore ,nl (我省略了一些部分,因为它们与讨论无关)

let data = [];

fetch("data/data.json").then(( response ) => {
    data = JSON.parse( response.text() );
});
//... later down the code
var store = new DataStore(data);

在这里,您在 fetch 调用的 data promise 链中重新分配 then 变量。尽管分配似乎可以工作,但现在位于 store.data 上的数据将是一个空数组,全局变量 data 现在将包含已解析的 response.text() 。您可能应该只插入刚刚解析的数据(但在我的示例中,我什至没有包含 DataStore,所以这仅供将来引用)

在您的 CodePen 中,您似乎为 Display 组件混合了 Prop 和状态。这本质上是一个无操作,除非你真的知道你在做什么,否则你不应该混合它们。另请注意,通过在 this.setState 生命周期方法中调用 componentWillReceiveProps ,应用程序将自动重新渲染超出需要的部分。我指的是这段代码:
componentWillReceiveProps(nextProps) {
  this.setState({
    color: nextProps.colorChoice,
    harmony: nextProps.harmonyChoice,
    harmonyColor: nextProps.harmonyColor,
    harmonyHex: nextProps.harmonyHex
  });
}

但是你会像这样渲染:
render() {
  return (
    <div>
      {/* these aren't changing even though states are being set */}
      <p><b>Color:</b> {this.state.color}</p>
      <p><b>Harmony:</b> {this.state.harmony}</p>
      <p><b>Harmony Color(s):</b> {this.state.harmonyColor} ({this.state.harmonyHex})</p>
    </div>
  )
}

在这里,您应该删除 componentWillReceiveProps 方法,并从 this.props 渲染值,因为您从 App 传递这些值。

替代解决方案

正如评论中提到的,您的代码目前在父组件和子组件之间传递状态的工作比它应该做的要多得多。

您应该记住的一件事是,当组件状态发生更改时,react 将自动重新渲染组件。当它发现虚拟 DOM 与真实 DOM 存在差异时,它会自动替换这些组件。

从这个意义上说,你的 DataStore 是没有必要的。根据您希望如何管理状态,组件将对这些更改使用react。

由于您的应用程序使用组件状态(这对于小型应用程序来说很好,一旦您想迁移到更大的应用程序,您可能会想要迁移到 Redux 或 MobX 之类的东西),您唯一需要做的就是让确保设置正确的组件状态以触发渲染。

例如,我以更简洁的方式重新编写了您的代码:

const Choice = ({ header, values, onChange, activeValue }) => {
  return <ul>
    <li><h1>{ header }</h1></li>
    { values.map( (value, key) =>	<li 
      key={key+value}
      className={classNames( { active: value === activeValue, item: true } )} 
      onClick={() => onChange( value )}>{ value }</li> ) }
  </ul>
};

const colors = ['red', 'green', 'black', 'blue', 'yellow'];
const harmonies = ['direct', 'split', 'analogous'];

class App extends React.Component {
  constructor(...args) {
    super(...args);
    this.state = {
      activeColor: undefined,
      activeHarmony: undefined
    };
  }
  onColorChanged( color ) {
    this.setState({ activeColor: color });
  }
  onHarmonyChanged( harmony ) {
    this.setState({ activeHarmony: harmony });
  }
  render() {
    let { activeColor, activeHarmony } = this.state;
    return <div>
      <Choice 
        header="Choose color" 
        values={colors} 
        activeValue={activeColor} 
        onChange={(...args) => this.onColorChanged(...args)} />
      <Choice 
        header="Choose harmony" 
        values={harmonies} 
        activeValue={activeHarmony}
        onChange={(...args) => this.onHarmonyChanged(...args)} />
    </div>;
  }
}

ReactDOM.render( <App />, document.querySelector('#container'));
h1 { margin: 0; padding: 0; }
ul {
  list-style-type: none;
}
.item {
  cursor: pointer;
  padding: 5px;
}
.active { background-color: lightgreen; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.2/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/15.6.2/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prop-types/15.6.0/prop-types.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/classnames/2.2.5/index.js"></script>
<div id="container"></div>


现在,此示例代码中有一些内容可能需要解释一下。一方面,此代码有 2 种组件类型,1 个称为 Choice 的无状态表示组件,以及一个称为 App 的容器组件,它将其状态委托(delegate)给它的子级。

关于容器和展示组件的更多信息可以在 Dan Abramov(redux 创建者)的 blog 上找到

上述概念的本质就是这样,App 组件负责状态,并与它的 child 共享它。因此,所有状态更改都需要在 App 组件上进行。正如您在渲染中看到的,App 只是简单地传递其状态:
render() {
  let { activeColor, activeHarmony } = this.state;
  return <div>
    <Choice 
      header="Choose color" 
      values={colors} 
      activeValue={activeColor} 
      onChange={(...args) => this.onColorChanged(...args)} />
    <Choice 
      header="Choose harmony" 
      values={harmonies} 
      activeValue={activeHarmony}
      onChange={(...args) => this.onHarmonyChanged(...args)} />
  </div>;
}
App将更改处理程序传递到Choice组件,可以在应发生选择时调用,这会转发到App,状态更改和应用程序重新渲染,允许Choice组件更新它的元素。
const Choice = ({ header, values, onChange, activeValue })

根据传入的 props,Choice 组件可以决定渲染时哪个是事件项。如您所见, Prop 已被破坏。 headervaluesonChangeactiveValue 都是组件的 props 上的属性,但是为了节省时间,我们可以将这些值分配给一个变量并在渲染中使用它们。

关于javascript - react .js : Parent state values not passing into child properties + Fetch API data cannot be accessed,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47726324/

相关文章:

javascript - 如何使用图标图像?

javascript - 我可以链接不返回这个的方法吗?

javascript - 动态 setState 在 react 中添加新的动态属性

reactjs - 如何在 react 查询中重新获取集合的单个项目

javascript - 获取资源,计算哈希值,返回 promise

TypeScript Fetch response.Json<T> - 预期 0 个类型参数,但得到 1 个

javascript - 如何在 Sencha Ext.data.Store 上创建分页?

ES6 类中的 javascript 'this' 返回未定义

javascript - 阵列没有看到新的更新

javascript - 使用 Promise 进行 fetch().then() 后页面保持刷新(设置了 e.preventDefault())