Even though printing items logs a populated array before the return function, it doesnt really render anything. I know for a fact its not a problem with improperly displaying the html. So i got suspicious and stringified it inside the return function to see if indeed the data im logging is there and to my dread i realised it isnt. As shown in the code, within the return function i get an empty array!
class Derp extends Component {
constructor(props){
super(props);
mainStore.subscribe(this.render.bind(this));
}
render(){
var items = mainStore.getState().itemReducer.items;
console.log(items); //yields an array of items as expected
return (
<div>
<span>{JSON.stringify(items)} </span> //yields [] in the DOM !!!!!!!
//when it should yield the same as above, a fully populated array
{
items.map(item =>
<div key={item.id}>
{item.name}
</div>
)
}
</div>
)
}
}
I've done this numerous times succesfully but this time around i just cant figure out what could be wrong with it.. Thanks for taking the time.
EDIT 1: I know this will seem cringeworthy ( because it is ) but the component is listening to all state changes like so : mainStore.subscribe(this.render.bind(this)); so it should always have access to updated data.
P.S: I am aware of dumb vs clever components and that im not using ReactRedux, im just experimenting and trying a few different things for curiosity's shake. This is an self-imposed "study" kind of code. This isnt meant for production or a project.
Return the div from your map function:
items.map(item =>
return <div key={item.id}>
item.name
</div>
)
Try escaping the content you wish to render:
items.map(item =>
<div key={item.id}>{item.name}</div>
)
There are two problems I see with your code:
You are trying to return more than one element. If you're before react16, you need to wrap those two elements in a div or something. If you're on react16, you can return them in an array.
You item.name needs to be in curly braces. Any JS within JSX markup needs to have curly braces. (This is how they know it's JS and not markup).
react16+
return [
<span>{JSON.stringify(items)} </span>,
...items.map(item =>
<div key={item.id}>
{item.name}
</div>
)
]
< react16
return (
<div>
<span>{JSON.stringify(items)} </span>
{items.map(item =>
<div key={item.id}>
{item.name}
</div>
)}
</div>
)
It seems like the problem was calling render directly. Instead calling forceUpdate() works fine. I am not 100% why this happens but i suspect that calling render on it's own doesnt mean much as it would probably need to be called in a React context in the React pipeline. I might be horribly off and if so please flag me and describe it a little better that i currently am able to.
Thanks everyone for helping.
Related
So I've been trying to figure this one out for a while and I'm really hoping someone would be able to help
I'm creating an array and storing a list of objects in it like this.
const heroPost = postList.filter(post => post.hero);
when I console log heroPost I get the following result.
I'm trying to access the url property in this object as follows.
console.log(heroPost[0].url);
But I keep getting this undefined error.
I'm not really sure what's going wrong here. I am using an async function to get data from a database which I'm guessing might be a problem but I'm outputting data from the 'postList' in jsx which I've used to retrieve data into the 'heroPost' so I'm uncertain how it can be the problem. Any help or ideas would be greatly appreciated.
Thank you!
Edit:
This is the portion of the component that uses the 'postList' to render data. Which works fine.
<div className="posts-container">
{postList.map((post) => (
<div key={post.id} className="post">
<h3 className="post-title">{post.title}</h3>
<div className="post-info">
<p className="author">{post.author}</p>
<p className="timestamp">{post.author}</p>
</div>
<p className="content">{post.body}</p>
<img src={post.url} alt="" />
</div>
))}
</div>
What I'm trying to do now is to use the heroPost to render data into the hero section of the page as follows but it is still throwing the undefined error.
<div className="hero">
<img src={heroPost[0].url} alt="" />
</div>
Thank you for all the comments and answers, I understand the problem now. Still don't really get why it works in the 'postList' instance and not here. Is there a way I can do this without directly trying to go to the file where I retrieve the data and add it into that function?
Since you are using an async function to get the data, when the component that consists this code, renders for the first time, it will not have the data it needs to render the component.
Hence 'undefined'
You can resolve this issue by conditionally rendering the component. That is, render the component only after the data is available.
Since you haven't shared the component code, you can refer this react doc to help with the issue : Conditional Rendering
Edit: following is how the condition would work. This is assuming postList is a state variable and a re-render will be triggered when it's value is changed:
For the second snippet you shared:
<div className="hero">
{(heroPost && heroPost[0])?<img src={heroPost[0].url} alt="" />:'No data'}
</div>
Simple answer: you get those error because it's undefined
If you look at you console.log you can see an empty array at the first line
That's because when you first render your component postList is an empty array.
Only after you fetch your data and populate you array you can access it
you can try to do this
useEffect(() => {
setHeroPost(postList.find(p => p.hero))
}, [postList])
I am using Redux to create a quiz app that includes a form with some nested fields. I have just realized (I think) that every key press to my input fields triggers a re-render if I use the children prop, i.e. designing the app like this:
const keys = Object.keys(state)
<QuizContainer>
{keys.map(key =>
<QuizForm key={key}>
{state[key].questions.map(({ questionId }) =>
<Question key={questionId} questionId={questionId}>
{state[key]questions[questionId].answers.map(({ answerId })=>
<Answer answerId={answerId} key={answerId} />
)}
</Question>
)}
</QuizForm>
)}
</QuizContainer>
QuizContainer is connected to redux with mapStateToProps and mapDispatchToProps and spits out an array of arrays that all have objects inside them. The store structure is designed according to Dan Abramov's "guide to redux nesting" (using the store kind of like a relational database) and could be described like this.
{
['quizId']: {
questions: ['questionId1'],
['questionId1']:
{ question: 'some question',
answers: ['answerId1', 'answerId2']
},
['answerId1']: { some: 'answer'},
['answerId2']: { some: 'other answer'}
}
The code above works in terms of everything being updated etc, etc, no errors but it triggers an insane amount of re-renders, but ONLY if I use the composition syntax. If I put each component inside another (i.e. not using props.children) and just send the quizId-key (and other id-numbers as props) it works as expected - no crazy re-rendering. To be crystal clear, it works when I do something like this:
// quizId and questionId being passed from parent component's map-function (QuizForm)
const Question ({ answers }) =>
<>
{answers.map(({ answerId }) =>
<Answer key={answerId} answerId={answerId} />
)}
</>
const mapStateToProps = (state, { questionId, quizId }) => ({
answers: state[quizId][questionId].answers
})
export default connect(mapStateToProps)(Question)
But WHY? What is the difference between the two? I realize that one of them is passed as a prop to the parent instead than being rendered as the child of that very parent, so to speak, but why does that give a different result in the end? Isn't the point that they should be equal but allow for better syntax?
Edit: I can now verify that the children prop is causing the problem. Setting
shouldComponentUpdate(nextProps) {
if (nextProps.children.length === this.props.children.length) {
return false
} else {
return true
}
}
fixes the problem. However, seems like a pretty black-box solution, not really sure what I am missing out on right now...
When you use the render prop pattern you are effectively declaring a new function each time the component renders.
So any shallow comparison between props.children will fail. This is a known drawback to the pattern, and your 'black-box' solution is a valid one.
Okay so I figured it out:
I had done two bad things:
I had my top component connected to state like so: mapStateToProps(state) => ({keys: Object.keys(state)}). I thought the object function would return a "static" array and prevent me from listening to the entire state but turns out I was wrong. Obviously (to me now), every time I changed the state I got a fresh array (but with the same entries). I now store them once on a completely separate property called quizIds.
I put my map-function in a bad place. I now keep render the QuizContainer like so:
<QuizContainer>
{quizIds.map(quizId =>
<QuizForm>
<Question>
<Answer />
</Question>
</QuizForm>
)}
</QuizContainer>
And then I render my arrays of children, injecting props for them to be able to use connect individually like so:
{questions.map((questionId, index) => (
<React.Fragment key={questionId}>
{React.cloneElement(this.props.children, {
index,
questionId,
quizId
})}
</React.Fragment>
))}
That last piece of code will not work if you decide to put several elements as children. Anyway, looks cleaner and works better now! :D
I have a simple dictionary app I'm making that returns search results. A search is queried on every onChange of the search field using a lokiJS in-memory database for the dictionary, so the results come very quick.
It is important for me to optimize the rendering of the results, so that the 50 or so filtered search results keep flowing as the user types and the queries happen on each key stroke.
Here's what I've been doing so far: (this works but is it the best/fastest way?)
Each (sync) query of the database returns an array of objects, which I then map with something like this:
queryDB(query) {
const results = queryLokiJsDB(query);
const resultsMapped = results.map((mpd) =>
<dl key={mpd["$loki"]} onClick={() => this.clickFunction(mpd.p, mpd.f)}>
<dt>{mpd.p} - {mpd.f}</dt> <dd>{mpd.e} <em>{mpd.c}</em></dd>
</dl>);
this.setState({ results: (
<div>
{resultsMapped}
</div>
)});
}
Then, once I have the results mapped like that, I add the mapped components to the state, and then the screen gets rendered with the new results.
render() {
return (
<div>
<SearchBarStuff />
<div className="results-container">
{this.state.results}
</div>
</div>
);
}
I made this when I was just learning React and I understand that people consider it really bad practice to store components in the state.
I was wondering what the best practice in terms of performance optimization would be (and code cleanness). Should I store the results array in state then render then map them into components in the render function? Would that cause any performance hits?
Another answer said that "A function in the render method will be created each render which is a slight performance hit."
And why is it so bad to store components in state? Is it just for code clarity? Keeping the state small? I appreciate your help and patience.
I'm not quite sure but I am thinking the same problem as #larz explained in comments. Also, as you mentioned the state will be clearer. So here what would I do if I were you.
First, set your state just with the results:
queryDB(query) {
const results = queryLokiJsDB(query);
this.setState({ results });
}
Then, map through the results but instead of creating your JSX immediately I would use a separate component. Pass it mpd (the element) and onClick function with its reference.
render() {
return (
<div>
<SearchBarStuff />
<div className="results-container">
{this.state.results.map( mpd => (
<Item key={mpd["$loki"]} mpd={mpd} onClick={this.clickFunction} />
) )}
</div>
</div>
);
}
Use the Item like that:
const Item = ( props ) => {
const { mpd, onClick } = props;
const handleClick = () => onClick( mpd.p, mpd.f );
return (
<dl onClick={handleClick}>
<dt>{mpd.p} - {mpd.f}</dt> <dd>{mpd.e} <em>{mpd.c}</em></dd>
</dl>
);
}
In this way, you are not using an arrow function in your onClick handler, so this function will not be recreated in every render since we use the reference.
I am trying to debug a component using Context API Consumer inside render method on browser dev tools. If i place break-point inside Consumer block, i can't print props etc. on console dynamically as this is undefined. Normally running this works fine, but while debugging only value of this is undefined. Following is sample render method of component.
componentMethod = () => {
console.log(this.props.name); //Placing breakpoint here, value is this is defined
}
render() {
return (
<div className={styles.container}>
<div>
<h4>{this.props.name}</h4>
</div>
<div className={styles.block}>
<MyComponent.Consumer>
{
({ firstParam, secondParam }) =>
<AotherComponent
firstParam={firstParam}
secondParam={secondParam}
thirdParam={this.props.name}
/>
}
</MyComponent.Consumer>
</div>
</div>
)
}
I could be related fat arrow use here, but I am able to get value of this while using break-point in componentMethod. Is there a way to bind this for Consumer block?
Try this, however, your question doesn't give enough context on what you are trying to solve. It would be better if you shared the Provider implementation as well and where you use it.
render() {
const { name } = this.props;
return (
<div className={styles.container}>
<div>
<h4>{name}</h4>
</div>
<div className={styles.block}>
<MyComponent.Consumer>
{
({ firstParam, secondParam }) =>
<AotherComponent
firstParam={firstParam}
secondParam={secondParam}
thirdParam={name}
/>
}
</MyComponent.Consumer>
</div>
</div>
)
}
It looks like you are interested in knowing what your consumer is passing down to your component during execution. There are two ways to accomplish it.
Hard Way
Let us breakdown how the consumer works (using your sample). That may help you with finding the right place to debug.
in your render() method, you have a <MyComponent.Consumer> call. The new Context Consumer API is built on the render-prop pattern.
Important thing to remember about the render-prop pattern is that: it is a function call. This function should return something which react can consider while rendering the tree.
Since it is a function call, you can put your console.log statements before your element. You will have to add an explicit return statement though.
As to why it is undefined in your method. I am assuming that the componentMethod is not a lifecycle method, so it is possible that this or this.propsis undefined based on how you are invoking that method. I do not see it invoked anywhere in your render method.
Eas(y/ier) Way:
Use react dev tools browser extension. You can look up all components by name. On clicking them you can see the props and state and even the context (as far as I remember). You can even change the values and see react react to it!
We have an object 'Schema' with inputs for a form. We map through the inputs and return a custom component for each that then does the rest of the work. The problem is that:
1) Custom components can't have a key attribute, as key is a special property used by React and is not passed to the children.
2) The first item in a list must have a key attribute so that React can know how to update the components.
If your first item in the list is a custom Component, then these two are mutually exclusive. We can get around this problem by surrounding the QuestionBlock with a div or any other generic element and adding a key attribute to it. However this results in code and HTML that looks sloppy, having excessive divs just to work around this problem. Is there any other cleaner way to do it. Below is the code we had before:
render() {
return (
<div className="App">
{Object.keys(Schema.inputs).map((key,index) => {
let data = Object.assign({}, Schema.inputs[key]);
data.index = index;
return <QuestionBlock key={index} {...data} />;
}}
</div>
);
}
Warning shown in console when using the above code:
Warning: QuestionBlock: key is not a prop. Trying to access it will
result in undefined being returned. If you need to access the same
value within the child component, you should pass it as a different
prop.
And below here is the code that works, but looks messy:
render() {
return (
<div className="App">
{Object.keys(Schema.inputs).map((key,index) => {
let data = Object.assign({}, Schema.inputs[key]);
data.index = index;
return <div key={index}>
<QuestionBlock {...data} />
</div>;
}}
</div>
);
}
Many thanks in advance!
Instead of wrapping using div, use a keyed Fragment.
Fragment is a container that doesn't generate HTML so it won't clutter your HTML output.
And also you can use a short-hand version <>, not <Fragment>