This question already has answers here:
NextJS getStaticProps() never called
(5 answers)
Closed 11 months ago.
I have having a rather frustrating time with Next Js and am hoping that someone will be able to help.
We have a component and I am trying to use "getInitialProps" to pass props to a component during the server-side rending process, however, nothing seems to come through. I'm pretty sure I've tried every permeation I can think of and the props never go through. I know that the getInitialProps is being fired because I see the console.log coming through in the command line.
const HeaderMain = (props) => {
return(
<>
<h1>props.name</h1> <!-- This is blank -->
</>
);
};
HeaderMain.getInitialProps = async ({ req }) => {
console.log({ req: req }) // this works
// Do some other stuff here
return {
name: "Paul"
}
}
export default HeaderMain
If anyone could give me a point in the right direction it would be greatly appreciated.
In the end, we have decided to send the data from the main page through props to the component. It took quite a bit of digging around to see how the rendering engine works to get it to work the way we wanted it to work.
Related
This question already has answers here:
OnChange event using React JS for drop down
(8 answers)
Closed 1 year ago.
I am very new to Reactjs. I am taking a course in Reactjs and trying by myself. So kindly excuse me.
In the below code I have two list boxes. I am selecting the postcode and based on the post code the other list box needs to be populated
In order to achieve this I used useEffect but made sure I passed empty array at the end so that it will not be called every time the state data and pc are updated
I read the other similar post and did #2.
Clearly the renders are in loop but which one?
I see many questions similar to mine in stackoverflow and out of which solution in #2 seems to be related to mine but I would like to know more about #4. Any leads are much appreciated. Thanks
function MainCard(){
const [data, setData] = useState({took: {}, timed_out: false, _shards: [], hits:
{total: {}, max_score: {}, hits: []}});
const [pc, setPc] = useState("");
useEffect(() => {
axios.get("http://localhost:9200/first_name/_search")
.then(response => {
setData(response.data)
});
}, []);
return (
<div>
<Card className="customCard">
<Card.Body>
<select name="postcode" id="postcode">
{data.hits.hits.map(hit => {
setPc(hit._id);
return hit;
})
.map(hit => <option value={hit._id}>{hit._id}</option>)
}
</select>
<select name="type-of-service" id="type-of-service">
{data.hits.hits.filter(hit => hit._id===pc)
.map(hit =>
<option value={hit._source}>{hit._source}</option>
)}
</select>
</Card.Body>
</Card>
</div>
)
}
export default MainCard;
You wrote very interesting code. The render layer and logic layer should be in this relationship.
Think -> Render
Which means your code should look like the following.
const App = () => {
// any logic i want to do
// do it
return (
// here's my render based on states
// try not to come up more logic here
)
}
You might ask how I can think again after the render. The think part has to be triggered by additional user actions. Otherwise you are writing a game which evolves with time, but React isn't a game machine, it's a UI library, the base case is to deal with a one time render upon user actions.
So how to fix your stuff.
<select onChange={onChange} />
Ask yourself if a user select a different option, what should I do, look for an event handler which does that.
I am trying to learn React by building a web application. Since I want to learn it step by step, for now I don't use Redux, I use only the React state and I have an issue.
This is my components architecture:
App.js
|
_________|_________
| |
Main.js Side.js
| |
Game.js Moves.js
As you can see, I have the main file called App.js, in the left side we have the Main.js which is the central part of the application which contains Game.js where actually my game is happening. On the right side we have Side.js which is the sidebar where I want to display the moves each player does in the game. They will be displayed in Moves.js.
To be more clear think at the chess game. In the left part you actually play the game and in the right part your moves will be listed.
Now I will show you my code and explain what the problem is.
// App.js
const App = React.memo(props => {
let [moveList, setMovesList] = useState([]);
return (
<React.Fragment>
<div className="col-8">
<Main setMovesList={setMovesList} />
</div>
<div className="col-4">
<Side moveList={moveList} />
</div>
</React.Fragment>
);
});
// Main.js
const Main = React.memo(props => {
return (
<React.Fragment>
<Game setMovesList={props.setMovesList} />
</React.Fragment>
);
});
// Game.js
const Game= React.memo(props => {
useEffect(() => {
function executeMove(e) {
props.setMovesList(e.target);
}
document.getElementById('board').addEventListener('click', executeMove, false);
return () => {
document.getElementById('board').removeEventListener('click', executeMove, false);
};
})
return (
// render the game board
);
});
// Side.js
const Side= React.memo(props => {
return (
<React.Fragment>
<Moves moveList={props.moveList} />
</React.Fragment>
);
});
// Moves.js
const Moves= React.memo(props => {
let [listItems, setListItems] = useState([]);
useEffect(() => {
let items = [];
for (let i = 0; i < props.moveList.length; i++) {
items.push(<div key={i+1}><div>{i+1}</div><div>{props.moveList[i]}</div></div>)
}
setListItems(items);
return () => {
console.log('why this is being triggered on each move?')
};
}, [props.moveList]);
return (
<React.Fragment>
{listItems}
</React.Fragment>
);
});
As you can see on my code, I have defined the state in App.js. On the left side I pass the function which updates the state based on the moves the player does. On the right side I pass the state in order to update the view.
My problem is that on each click event inside Game.js the component Moves.js unmounts and that console.log is being triggered and I wasn't expected it to behave like that. I was expecting that it will unmount only when I change a view to another.
Any idea why this is happening ? Feel free to ask me anything if what I wrote does not make sense.
Thanks for explaining your question so well - it was really easy to understand.
Now, the thing is, your component isn't actually unmounting. You've passed props.movesList as a dependency for the usEffect. Now the first time your useEffect is triggered, it will set up the return statement. The next time the useEffect gets triggered due to a change in props.movesList, the return statement will get executed.
If you intend to execute something on unmount of a component - shift it to another useEffect with an empty dependency array.
answering your question
The answer to your question
"why this is being triggered on each move"
would be:
"because useEffect wants to update the component with the changed state"
But I would be inclined to say:
"you should not ask this question, you should not care"
understanding useEffect
You should understand useEffect as something that makes sure the state is up to date, not as a kind of lifecycle hook.
Imagine for a moment that useEffect gets called all the time, over and over again, just to make sure everything is up to date. This is not true, but this mental model might help to understand.
You don't care if and when useEffect gets called, you only care about if the state is correct.
The function returned from useEffect should clean up its own stuff (e.g. the eventlisteners), again, making sure everything is clean and up to date, but it is not a onUnmount handler.
understanding React hooks
You should get used to the idea that every functional component and every hook is called over and over again. React decides if it might not be necessary.
If you really have performance problems, you might use e.g. React.memo and useCallback, but even then, do not rely on that anything is not called anymore.
React might call your function anyway, if it thinks it is necessary. Use React.memo only as kind of a hint to react to do some optimization here.
more React tips
work on state
display the state
E.g. do not create a list of <div>, as you did, instead, create a list of e.g. objects, and render that list inside the view. You might even create an own MovesView component, only displaying the list. That might be a bit too much separation in your example, but you should get used to the idea, also I assume your real component will be much bigger at the end.
Don’t be afraid to split components into smaller components.
It seems the problem is occurred by Game element.
It triggers addEventListener on every render.
Why not use onClick event handler
/* remove this part
useEffect(() => {
function executeMove(e) {
props.setMovesList(e.target);
}
document.getElementById('board').addEventListener('click', executeMove, false);
})
*/
const executeMove = (e) => {
props.setMovesList(e.target);
}
return (
<div id="board" onClick={executeMove}>
...
</div>
)
If you want to use addEventListener, it should be added when the component mounted. Pass empty array([]) to useEffect as second parameter.
useEffect(() => {
function executeMove(e) {
props.setMovesList(e.target);
}
document.getElementById('board').addEventListener('click', executeMove, false);
}, [])
I’m pretty new to this so apologies in advance if I'm being dumb. I’m building a react application on top of the WordPress rest API. I’m trying to do something pretty basic which is to create a component showing a list of pages, each with a link which takes the user to a new view showing the individual ‘page’ with all the data for that page.
I’m almost there but am having problems outputting the correct data on the individual pages.
The approach I’ve taken is to take the id from match.params and then match it up with the page id passed down through props using javascript ‘find’.
This kind of works. I can console log the data for the individual page out from inside the ‘getPage’ method in the PageSingle component if I call it in the render method but the moment I try to access any individual values such as the id I get the old Cannot read property 'id' of undefined.
Probably not very clearly explained so please see code below:
PageList.js
import React from 'react';
import { Link } from 'react-router-dom';
const PageList = (props) => {
const pages = props.pages.map( (page) => {
return (
<div key={page.id}>
<Link to={`/pagelist/${page.id}`}>{page.title.rendered}</Link>
</div>
);
});
return <div className="page-list">{pages}</div>
};
export default PageList;
PageSingle.js
import React from 'react';
class PageSingle extends React.Component {
getPage = (props) => {
let thePage = props.pages.find(page => page.id === Number(props.match.params.pageId) );
**console.log(thePage); // 1. this works
console.log(thePage.id); // 2. this leads to error**
return thePage;
};
render() {
this.getPage(this.props);
return (
<h4>PageSingle</h4>
)
}
};
export default PageSingle;
JSON shown in console when it works – I’ve removed some so as not to take up too much space but you get the idea
{
content: {rendered: "Test Page 2 Text. Test Page 2 Text. Test Page 2 Text. Test Page 2 Text. Test Page 2 Text. ", protected: false}
date: "2019-09-30T13:38:47"
excerpt: {rendered: "Test Page 2 Text. Test Page 2 Text. Test Page 2 Text. Test Page 2 Text. Test Page 2 Text.", protected: false}
id: 14
link: "http://localhost/all_projects/wordpress/sites_main/my_projects/portfolio/wordpress/test-page-2/"
slug: "test-page-2"
status: "publish"
title: {rendered: "Test Page 2"}
type: "page"
__proto__: Object
}
The props are sent to page single using Browser Router. The routes themselves are defined in the App.js component and look like this. Not sure if this is relevant or not, probably not.:
Routes
<Route
path="/pagelist" exact
render={ (props) => <PageList {...props} pages={ this.state.pages } /> }
/>
<Route exact
path="/pagelist/:pageId"
render={(props) => <PageSingle {...props} pages={ this.state.pages } /> }
/>
Obviously, the end goal is to eventually display the relevant data via the render method but I actually need to access that data before I can do that.
It’s probably something really basic that I’m just not understanding.
Any pointers in the right direction would be much appreciated.
Thanks.
If you have the correct data in your console.log then I think this is because console.log executes after a small delay,
Try doing this and see if you get the property
setTimeout(function()
{
console.log(thePage.id)
}, 100);
usually keys are added after the console.log call
Hope it helps
Okay, for anyone interested, the answer lay in the fact that an array with an object inside of it was being returned in PageSingle.js.
The answer therefore lay in grabbin gthe correct page with an if statement and pushing the required values to an array and then accessing that with bracket notation:
let thisPage = [];
props.pages.map((page) => {
if (page.id === Number(props.match.params.pageId)) {
thisPage.push(page.id, page.title.rendered, page.content.rendered, page.featured_images);
}
return thisPage;
})
return (
<div>
<h4>{thisPage[1]}</h4>
<p>{thisPage[2]}</p>
</div>
)
Got to be a less convoluted way of doing this though so, any suggestions please let me know.
The Problem
I have an application that uses this React Redux Boilerplate: https://github.com/flexdinesh/react-redux-boilerplate
I created a new page that is connected to the injected reducer + saga.
I receive following props: posts, loading, error, loadPosts and match
When I use these directly the app is working as expected. But as soon as I start to destructure the props, the app is behaving unexpectedly.
Especially with the match props.
When I do it like this:
const SubforumPage = (props) => {
useEffect(() => {
const { id: subId } = props.match.params;
console.log('props: ', subId);
}, []);
// .... other code
}
No problem everything works.
But when I do it like this:
const SubforumPage = ({match}) => {
useEffect(() => {
const { id: subId } = match.params;
console.log('props: ', subId);
}, []);
// .... other code
}
match suddenly gets undefined!
I have really no clue what so ever why this is happening. It's the first time that I see an error like this.
This specific page is set up like this in the routing file:
<Route path="/sub/:id" component={SubforumPage} />
And it's clearly working when using (props) in the function arguments but not with ({match})
Why is this? Can please someone help me out here.
What have I tried?
I continuesly started destructuring one prop after another. At first this approach works and it's still not undefined but when I get to some props, it's different which ones, it will stop working.
I think it has to do something with how I use my useEffect() hook?
I pass an empty array so it does just run when mounting. It seems like when I refresh the page, the posts are cleared out but the useEffect doesn't run anymore, so the new posts doesn't get fetched. Because hen also the console.log inside the useEffect hook is undefined doesn't even run. But for example the loading prop in console.log outside of useEffect is indeed not undefined
(But that still does not explain why it's working with (props) as argument).
Am I just using useEffect wrong?
Many thanks
Ok guys that was completely my fault. Guess I'm too tired :D. Here is what caused the problem:
I fetch my post in the useEffect hook. I also render a component where I pass in the posts. But the posts are not available because the component has to wait for the data to come in. So I completely forgot that I have to wait for the data.
Before:
return <PostsGroup posts={posts} />;
After: (correct)
return <PostsGroup posts={posts || []} />;
I had a check in place looking like this:
if (loading) return <CircularProgress />;
(before the other return). But it doesn't matter because loading is false when the component initially renders.
So I also set the initial value from loading to true (in my initialState of the reducer). So I have now two checks in place.
Sorry guys. So stupid.
I can see my data the first time I navigate to a different route via a link:
<Link to={{ pathname: `/settings/${door._id}` }}>
Inställningar
And it works fine, but when I refresh the page my application tries to use data that haven’t been fetched yet, therefor it crashes. I tried to add a loading screen for my reducer, but that didn’t do the trick.
Here’s my reducer I created a gist for it: https://gist.github.com/Martinnord/c944f4fd22f4a4ff7e3e0b362e7d4e29
I also reached out to some forums and they gave me a tip to go to the very root of my app before ReactDOM.render and in there, connect to redux state and have an item called dataIsLoading or something and if dataIsLoading is true I will show a loading thing and if it’s not it renders all my components. Because otherwise I will run into this issue where the selector depends on data in redux, and I can't control when mapStateToProps runs outside of just not rendering that component at all.
But the problem is that I have absolutely no idea how to do that, I’ve been trying for hours with no luck. If someone could give me any guidance or tips I would be blessed. And if more code from me is needed, please just let me know.
Thanks for reading.
Update
Got a request from the comments so I will add some code.
mapStateToProps
const mapStateToProps = (state, ownProps, loading, fetchDoorsReducer) => {
const door = getDoorById(state, ownProps.match.params.itemId)
const doorId = ownProps.match.params.itemId
const controller = getControllerById(state, door.controller)
return {
doors: door,
controllers: controller,
isLoading: state.fetchDoors.isLoading
}
}
How I use isLoading: if (this.props.isLoading) return <p>laddar</p>
It's a bit unclear what piece of the functionality you describe you're having difficulty translating to code. But in case this is it, here's an example that might help you;
LoadingWrapper = React.createClass({
render() {
if (this.props.isLoading) {
return <SomeLoadingIconHere/>;
} else {
return this.props.children;
}
}
});
and then elsewhere...
render() {
return <LoadingWrapper isLoading={this.props.somePropertyFromRedux}>
<SomeComponent/>
</LoadingWrapper>;
}