I have created a React Component that renders a set of sub-elements given an array of ids. The array of ids is kept in the state of the parent component, and then I run some ajax calls based on the ids to fetch data to render. The fetched data is stored in a separate data array in the state. The rendered components use the id as key.
The ids can change based on actions outside of the component, so I use setState on the component to replace the array. The updated id-state will probably contain some of the same ids as the in the original array. At the same time I empty the 'data array' so that everything will be rendered again.
When I do this I sometimes get the key-warning:
Warning: flattenChildren(...): Encountered two children with the same key. Child keys must be unique; when two children share a key, only the first child will be used.
The new array does not contain any duplicates. So why does it happen, and what can I do to avoid this?
Edit: Added some code by request. Note: I am using the Infinite Scroll module. Could this be causing it?
getInitialState: function() {   return {     hasMore: true,     num: 0,     movieIds: this.props.movieIds,     movies: []   }; },   render: function() {   var InfiniteScroll = React.addons.InfiniteScroll;    return (     <InfiniteScroll         pageStart={0}         loadMore={this.loadMore}         threshold='20'         hasMore={this.state.hasMore}>         <ul className="movieList">           {this.state.movies}         </ul>     </InfiniteScroll>        ); }   comp = this; $.ajax( {   url: url,   contentType: "json",   success: function (data) {     var m = createMovieLi(data);     var updatedMovies = comp.state.movies;     updatedMovies[num] = m;     comp.setState({movies: updatedMovies});   } });   And finally when updating outside the component:
movieBox.setState({   hasMore: true,   num: 0,   movieIds: filteredIds,   movies: [] }); 
                As we already saw before, React re-renders a component when you call the setState function to change the state (or the provided function from the useState hook in function components). As a result, the child components only update when the parent component's state changes with one of those functions.
Each state change in the parent component triggers re-rendering of all its subsequent child components even if no props are changed.
To change child component's state from parent component with React, we can pass props. const Child = ({ open }) => { return <Drawer open={open} />; }; const ParentComponent = () => { const [isOpen, setIsOpen] = useState(false); const toggleChildMenu = () => { setIsOpen((prevValue) => !
Sending state/props to another component using the onClick event: So first we store the state/props into the parent component i.e in which component where we trigger the onClick event. Then to pass the state into another component, we simply pass it as a prop.
I figured out my mistake, and it had nothing to do with React per se. It was a classic case of missing javascript closure inside a loop.
Because of the possibility of duplicates I stored each ajax response in window.localStorage, on the movieId. Or so I thought.
In React Inifinite Scroll each item in your list is drawn sequentially with a call to the loadMore-function. Inside this function I did my ajax call, and stored the result in the browser cache. The code looked something like this:
  var cachedValue = window.localStorage.getItem(String(movieId));   var cachedData = cachedValue ? JSON.parse(cachedValue) : cachedValue;    if (cachedData != null) {     comp.drawNextMovie(cachedData);   } else {      $.ajax( {       type: "GET",       url: this.state.movieUrl + movieId,       contentType: "json",       success: function (movieData) {         window.localStorage.setItem(String(movieId), JSON.stringify(movieData));         comp.drawNextMovie(movieData);       }     });     }       Can you spot the mistake? When the ajax-call returns, movieId is no longer what is was. So I end up storing the data by the wrong id, and get some strange React warnings in return. Because this was done inside the loadMore function called by the InfiniteScroll-module, I was not aware that this function was not properly scoped.
I fixed it by adding a Immediately-invoked function expression.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With