I'm kind of new to React js. I have two questions. Here I go
Question 1
I wanted to update(re-render) the child component when I update(re-render) the parent component. After some googling, I found out that when the props are changed and we call the forceUpdate function only the parent component is updated not the child component. If I wanted to update the child component I needed to needed to use setState function and set something in the state to update the child. But the problem that I'm facing is that when I set state in the parent component the child component is not updated. The render method of the child is not called. Can somebody please tell me what is it that I'm doing wrong?
Parent.jsx
class Application extends React.Component {
isBindEvents=false;
componentWillMount(){
let {dispatch} = this.props;
dispatch(getCompanyInfo( "abcd.com", (res) => { // THIS IS A ACTION CALL
this.props.user = res.data.user;
this.setState({name:res.data.user.name})
this.forceUpdate()
}))
}
render(){
return ( <div className='react-wrapper'>
<ABCD {...this.props} /> // THIS IS THE CHILD COMPONENT WHICH I WANT TO RE-RENDER WHEN THE PARENT COMPONENT CHANGES
<div >
<div id="body" >
<div>
<div >
<div className="container-fluid">
{this.props.posts}
</div>
</div>
</div>
</div>
</div>
</div>)
}
}
export default connect(mapStateToProps)(Application)
CHILD.JSX
class ABCD extends React.Component {
render() {
let isShowUser = this.props.user
? true
: false;
return (
<div> Here is the user name {isShowUser? this.props.user.name: 'user not present'} </div>
);
}
}
export default connect(mapStateToProps)(ABCD);
Now what I'm doing is that when the application component is mounting I generate an ajax call to my backend server and when it return it updates the props and I set the state so that that child component is also rerendered but the child component is not re-rendering. Can somebody please tell me what's is going wrong.
Question 2
The next question is related to react router I'm using react router for the routeing.This is how I'm using router
module.exports = {
path: '/',
component: Application,
indexRoute: require('./abcd'),
childRoutes: [
require('./componentTwo'),
require('./componentOne'),
]
};
Now let's suppose I'm going to component Two route which will render component Two and I generate a ajax call in application component and on the basis of the data returned by the ajax call I set some props in the application component and I also want the component Two to re-render as soon some props are changed in application is there any way to do that
Thanks any help will be appreciated
this.props.user = res.data.user;
You can't assign to props. Props are passed from a parent. Set the user in the state and pass the state to your child component like so:
<ABCD {...this.props} user={this.state.user} />
In your child component you will now have access to this.props.user. Also the this.forceUpdate() will not be needed then.
Related
I want to pass a simple string, number or boolean up more than one level in my component tree. From what I read I need to do this with callback functions but I can't seem to get the logic right.
Here is a sample of where I pass a prop down from Parent App to grandchild Breadcrumb. I would like this prop to actually come from the last child in the tree, the "ResultsPage" component.
I realise there are better ways of doing sth like this (redux, context, different structure, etc), the point here for me is learning and to understand how to use callback functions and how to pass a prop up several more than 1 level.
Newbie friendly please - thanks for any input :)
class App extends React.Component {
render() {
return (
<>
<h1>Top level app</h1>
{/* I import the header and pass down prop */}
<Header currentLocation="Results Page" />
{/* I import the main app content */}
<ResultsPage />
</>
);
}
}
function Header(props) {
return (
<>
<h2>
This is the header element. It will have some nav items and the
breadcrumb I import
</h2>
{/* I import the breadcrumb accept the props from parent and pass the props down to child */}
<Crumbs currentLocation={props.currentLocation} />
</>
);
}
function Crumbs(props) {
return (
<>
{/* I display the props I passed down through the tree */}
<h3>
<small>This is the breadcrumb, you are on</small>{" "}
{props.currentLocation}
</h3>
</>
);
}
function ResultsPage() {
return (
<>
<p>
This is the actual results content. I would like this component to tell
the header component that I have loaded so it can update the breadcrumb
to let it know which page is currently loaded in the app.
</p>
</>
);
}
export default App;
To complete this issue I lewave the following solutions:
Codesandbox: Solution to the initial question
Codesandbox: Additional solution for the same problem using only functional components
Hope it helps the next guy :)
Maintain a local state variable to store the location, and pass a callback function through the props to set it.
class App extends React.Component {
constructor(props){
super(props);
this.state = {
currentLocation : "InitialLocation"
}
}
changeCurrentLocation = (newLocation) => {
this.setState({currentLocation : newLocation})
}
render() {
...
<ResultsPage callback={this.changeCurrentLocation}/>
}
}
The changeCurrentLocation function takes the new location as argument and modifies the state. Everytime the state changes, the render function is called again. This would refresh the view with updated state information, in your case - currentLocation.
function ResultsPage({ callback }) {
useEffect(() => {
callback('My Results');
}, [callback])
return (
...
);
}
Best way is to do this as I think keep the state inside a redux store. You can create a listener using subscribe() method in the redux to listen any dispatches from the child components from the parent component.
Also there is some easy method, You can use localstorage. You can store value from the child component and listen it by the parent component using window.addEventListener('storage', function(e) { } callback method. I hope you can understand what I tried to say.
Ok so this question is a bit tricky. I have been thinking about whether this is even correct concept wise, considering React is supposed to be a one-way flow of data, from parent to children, and not viceversa. But I would like to post the question anyway so I get different opinions and even possibly a way to get this to work.
In my app, I have a pretty large component that accepts forms as its children, and does some nifty React magic to pass its methods to the children so when the children elements are changed, they trigger the parent components methods that store the data in state and handles the form submissions. It works very nicely, however it is not so good at catching "defaultValues".
In a nutshell, I'm trying to trigger my parent method on the chilren's componentidMount() method, and it works, however, if there's more than one child trying to do this, the method gets called twice but it only uses the second child's dataset.
I have created a simplified version of my issue in the following code:
import React from 'react'
export class Parent extends React.Component {
constructor(props){
super(props)
this.state = {
data : {name:'james'}
}
this.updateData = this.updateData.bind(this)
}
updateData(key,data){
console.log('updating data')
this.setState({
data : {...this.state.data,[key]:data}
})
}
render(){
console.log(this.state)
return (
<div>
<Child1 updateData={this.updateData}/>
<Child2 updateData={this.updateData}/>
</div>
)
}
}
class Child1 extends React.Component {
componentDidMount(){
this.props.updateData('child1','myData')
}
render(){
return (
<div>
I am Child 1
</div>
)
}
}
class Child2 extends React.Component {
componentDidMount(){
this.props.updateData('child2','myData2')
}
render(){
return (
<div>
I am Child 2
</div>
)
}
}
This code will render 'updating data' twice on the console, but it will only update the state with the data sent in child2. Again, I can see how this may not be the best approach considering that im setting the state of a parent from its children, but it would be a good solution for setting default values on a parent component that gets reused a lot with different children.
Im all ears stack overflow
I think the problem is that setState does both updates at the same time (batches them) meaning the same initial state is used when merging both partial states. You need to use updater function as shown by kind user:
this.setState((prevState) => ({ data: { ...prevState.data, [key]: data } }));
I don't know if that even exists. I am trying to reuse a component, however, the component I am trying to reuse receive props and already handles them inside of the component.
If I reuse this component in another place I am going to have to change all of the props received.
Is that a common thing when developing in React or I am doing something wrong?
I think you should divide the component with the props to two:
Component A. functionality (and all the props needed)
Component B. a type of container with no props just holding the component A
Hope this helps you!
Yes is common and is often resolved with HoC (High Order Components)
ref: https://reactjs.org/docs/higher-order-components.html
Example
function logProps(WrappedComponent) {
return class extends React.Component {
componentWillReceiveProps(nextProps) {
console.log('Current props: ', this.props);
console.log('Next props: ', nextProps);
}
render() {
// Wraps the input component in a container, without mutating it. Good!
return <WrappedComponent {...this.props} />;
}
}
}
const EnhancedComponent = logProps(WrappedComponent);
I created a topbar menu thats needs access to a react component. Inside the topbar file I do not render the other components, but would like to access a function inside one of the components I use.
The project structure looks like this:
Header Component
TopBar Component
LayoutWrapper Component <-- Here I render other components
CustomerDetails Component <-- Here sits the functon I want to call.
This is the TopBar File:
class AdminTopbar extends Component {
renderTopMenu() {
...
if (currentPage.projects || currentPage.customers || currentPage.activities) {
return(
<nav>
...
<li>
// Function needs to be called here
{menuPageType == null ? null : <button onClick={updateActivityCardDetails.bind(this)}>Archiveren</button>}
</li>
</nav>
);
}`enter code here`
}
render() {
return (
<div className="topbar clear">
...
</div>
);
}
}
export default withRouter(AdminTopbar);
ActivityCardDetails file where the function sits:
class ActivityCardDetails extends Component {
constructor(props){
super(props);
// set page title
document.title = 'Strippenkaarten overzicht';
}
updateActivityCardDetails() {
}
}
I found some posts about refs to the parent but I don't have a nested structure between those files.
As a recap: On the TopBar components, which is a separate components without any relations, I would like to call the updateActivityCard method that sits in the ActvityCardDetails components.
React has recently(ish added the context API ) https://reactjs.org/docs/context.html
You'd probably be best off breaking the function out of the component because it's used by different components who aren't strictly hierarchically related. Then just use context to access it from each component.
You can try to wrap your whole structure with a wrapper component and follow the instruction given on each line below:
Wrapper Component <-- Here place a state and a custom function that can set a callbackFunction to its state e.g. myFunc = (callback) => this.setState({updateFunction: callback})
Header Component
TopBar Component <-- Pass the Wrapper Component's state here through props, like state={this.state}
LayoutWrapper Component <-- Pass the Wrapper Component's custom function here through props
CustomerDetails Component <-- Pass the Wrapper Component's custom function here through its parent's props, and call it like 'this.props.myFunc(this.yourInnerUpdateFunction)' on DidMount
Once you're done with this, you should be able to call the updateFunction() from your TopBar component through 'this.props.state.updateFunction()'
P.S. It's not an ideal approach, but it can get the job done if your app isn't too heavy. Hope this helps.
I'm trying to create a wrapper that does not mount the child if the user is not authenticated. Otherwise, it mounts and renders the child component.
Roughly looks like this:
export class RedirectOnCondition extends Component {
render(){
return this.props.isAuthenticated? this.props.children : null
}
}
My issue is the the child still mounts before the parent has a chance to evaluate the condition. It's only after the child's componentWillMount` (and any associated API calls have fired and failed) that the parent's render kicks in and remove's the child. According to this question this is how React works.
How can I get around this?
In the first render, the parent component may have not yet received the props you need to process the conditional rendering of a children component. In this case, you may want to check first if the props is already there.
export class RedirectOnCondition extends Component {
render(){
return "isAuthenticated" in this.props ? this.props.isAuthenticated? this.props.children : null : null
}
}