Best way to pass same data to two React components? - reactjs

I currently have two React components one that maintains a user inputted list of objects ex: [{"good1":"$10"},{"good2":"$15"}] and another component that lets the user create a list and use the goods as tags. However I am having trouble on how to pass data that is in the state of GoodManage to ListManager, I also have a wrapper for the layout. I tried passing the state from the LayoutManager into the GoodManager and having it update the goods list, and send that same list to ListManager, but it only seems to work once.
class LayoutManager extends React.Component{
constructor(){
super();
this.state = {"goods":[{"good1":"$1"}]};
}
updateGoods(goods){
this.setState({"goods":goods});
}
render(){
return (
<div className="layout-manager">
<div className="good-manager container">
<GoodManager update={this.updateGoods.bind(this)} goods={this.state.goods}/>
</div>
<div className="list-mananger container">
<ListManager goods={this.state.goods}/>
</div>
</div>
)
}
}

As you described its working only first time, it means you are storing the goods value in the state variable of ListManager component in constructor, it will save only the initial list since constructor only get called on first rendering not on re-endering, so you need to use lifecycle method componentWillReceiveProps, it will get called when any change happen to props values, at that time you need to update the state values, use this method in ListManager component:
componentWillReceiveProps(nextProp){
this.setState({goods: nextProps.goods});
}
Reference: https://facebook.github.io/react/docs/react-component.html#componentwillreceiveprops

Related

ReactJS: How to render a collection of objects

So I'm quite new on web development last couple of days. I come from c++ background and I can't wrap my head through all the principles of reactjs. I have 2 classes. The child class called JobAd should render some information that it got from props.
export default class JobAd extends Component {
constructor(props) {
super(props);
this.state ={
index: props.index,
id: props.jobId,
name: props.name,
description: props.description,
location: props.location,
adress: props.adress,
alreadyApplied: props.alreadyApplied,
open: false,
// toggleJob: props.toggleJob,
};
this.toggleJob = props.toggleJob;
}
render() {
return (
<div className={`${styles.jobAd} d-flex` + "job " + (this.state.open ? 'open': '')} key={this.state.index} onClick={() => this.toggleJob(this.state.index)}>
<div className={`${styles.jobTitle}`}>
{this.state.location} - {this.state.name}
</div>
<div className={`${styles.jobDetails}`}>
<div className={`${styles.jobDescription}`}> {this.state.description}</div>
<div className={`${styles.jobAdress}`}>{this.state.adress}</div>
<ApplyButton jobId= {this.props.id} alreadyApplied = {this.props.alreadyApplied}/>
</div>
</div>
)
}
}
The second class, queries a mongoDB db and creates jobAd objects populating them from the info gotten from db.
class JobExplorer extends React.Component
{
...
result.data.jobs.forEach(job => {
var find = job.employees.find(obj => obj === userId);
if (!(find === undefined)) {
alreadyApplied = true;
}
var toPush = new JobAd ({
index: i,
id:job._id,
description:job.description,
name:job.name,
location:job.locationName,
adress:job.locationAdress,
alreadyApplied:alreadyApplied,
open:false,
toggleJob: this.toggleJob.bind(this)
});
jobList2.push(toPush);
console.log("look");
console.log(jobList2)
});
this.setState({
jobList: jobList2
})
this.setState({
error: null,
jobs: result.data.jobs
});
...
render()
{
console.log("look2");
console.log(this.state.jobList);
return (
<div><Navigation />
{this.state.jobList}
</div>
);
}
But I am faced with the following error which I cannot find a fix for.
Error: Objects are not valid as a React child (found: object with keys {props, context, refs, updater, state, toggleJob}). If you meant to render a collection of children, use an array instead.
How should I instantiate those objects so I could render them using the "architecture" I wrote. Is there a fundamental flaw that I have in my classes?
The below snippet doesn't work because new will return an object (this) not the react component.
So, instead of
var toPush = new JobAd({
index: i,
id: job._id,
...
});
jobList2.push(toPush);
you can do this
var toPush = <JobAd
index={i}
id={job._id}
...
/>;
The above snippet works because <JobAd ... /> is converted to React.createElement(JobAd, ... ). However, you still shouldn't do it like this. since there are a lot of better ways to do this. one of them is:
save just the data in joblist and then render the data list on JobAd component
like below:-
render(){
return this.state.joblist.map((job, i) => (
<JobAd
key={job._id}
index={i}
...
/>
));
}
The key is a really important thing. Read about it: https://reactjs.org/docs/lists-and-keys.html
Things that could be improved:-
Don't copy props in the state as you are doing in JobAd class instead directly render the props.
Don't call setState twice as in JobExplorer. you could set all the keys in
setState at the same time. since that would render the component twice.
Suggestions:-
You should avoid using var as that might cause some issues here.
since, you are just a starter, try using functional component first. they are
quite easier to grasp
You seem to have a misconception about state/props in React and web development. It's very normal; I learned python and Java first and many tutorials seem to assume that people just know this already.
"State" in generally refers to variables containing/referring to values that can change without a page refresh in your application. If you know a value is not going to change, it does not need to be held in state. Storing it in a normal variable is exactly what you should do.
"Props" is just another word for arguments that are passed to React components. There's more to it in reality, but as a beginner, that's all you need to really know for now.
So in your job add, things like name, address, jobs, description shouldn't go in state because they aren't going to change as a result of user interaction or for any other reason, unless the underlying data they are loaded from changes, but then that wouldn't be handled by React but instead by the API that your app gets data from. They should just be rendered, so refer to them like this.props.address in your render method. The value for open, however, need to be in state, because that definitely can change.
As for the error, it looks like you are not calling JobAd correctly. You need to use the syntax <Job Ad/> rather than new JobAd...that won't work in React.
I would recommend doing a tutorial to get the basics down.

How to store the information in react component.

When I am asking this question, lots of doubts are coming into my mind. well, first I will give my problem description.
I have component X. and it contains checkboxes and a search box.
while something typed (call it search_query) in search box,
X needed to update the checkboxes which matches the search_query. [note that I got all the values of checkboxes by some api call. and it is done when component created. ]
First doubts I came to my mind is that
store (search_query) and (values of checkboxes) in component state
if the values are more searching takes more time.
is it possible to change the values of props inside the component
or is there any other way to do it ??
Since no code is shared. Assuming you are using plain React ( no Redux, middlewares are used ).
Answering your question:
[1] Is it possible to change the values of props inside the component?
Changing props values inside the component is a bad approach.
"All React components must act like pure functions with respect to their props."
[ref: https://facebook.github.io/react/docs/components-and-props.html#props-are-read-only]
Also, the view doesn't get the update if props values changed within the component.
[2] or is there any other way to do it.
yes ( without mutation inside the component )
using "state" property to hold values & setState to update values.
[3] How to store the information in react component?
Let's rename component X as FilterData,
searchbox ( SearchBox ) & checkboxes (SelectionBox) are two individual components.
// Define state to FilterData
class FilterData extends React.Component {
constructor() {
super()
this.state = {
term: '',
searchResults: []
}
}
....
}
// pass the state values to child components as props
class FilterData extends React.Component {
....
render() {
return (
<div>
<SearchBox term={this.state.term} />
<SelectionBox options={this.state.searchResults} />
</div>
)
}
}
In React App,
data flows top down (unidirectional) and there should be a single source of truth.
SearchBox & SelectionBox are two individual (sibling) components,
SearchBox's state has terms ( which has the search string )
When the user enters input SearchBox will update its state and possible to detect change event and fire ajax and get the response.
How SelectionBox can detect search that had happened, how it can get data.
This is why the state is moved to common ancestor FilterData.
[Ref: https://facebook.github.io/react/docs/lifting-state-up.html]
[Ref: https://facebook.github.io/react/docs/state-and-lifecycle.html#the-data-flows-down]
Code Sample -----------------------------------------------------
Selected values are not saved:
https://codepen.io/sudhnk/pen/NgWgqe?editors=1010
Selected values are saved:
https://codepen.io/sudhnk/pen/JJjyZw?editors=1010

In React how do I have my child tell the parent its key?

In React we are loading a list of children components that define their own ID. We want to then do a pass where we re-arrange the children based on their set internal ID. How do we communicate the internal ID to the parent? Also it would be nice to use that ID as the parent rendering ekey. Note the internal ID does not match the component name.
class Thing1 extends React.Component {
const ID = 'Thing1IDString';
}
class Thing2 extends React.Component {
const ID = 'Thing2IDString';
}
<Parent>
<Thing1 />
<Thing2 />
<Thing3 />
</Parent>
The first thing I'd suggest is trying to make the parent know how to compute the child IDs somehow. If the child IDs come from a database, then make the parent compute them instead of making the children compute them. The lower components in the tree should be less intelligent. Hand them everything they need to know in its final form, so they can simply render it.
The structure you're talking about, where the child has "private" data that the parent needs to act upon, is generally solved in React by:
Storing that data in the parent
Passing the data to the child as a prop
Passing an onDataChanged function to the child, so that the child can tell the parent when the data changes.
This is the "controlled input" pattern. It feels awkward at first, and may seem like there's too much indirection at first glance. But the advantage is that the data flow is very predictable.
Now, if that isn't possible in your case, and you really need the children to "register" with the parent, then you can use props or context to pass a registerChild function to the children. In the child, use its componentDidMount lifecycle method to call the registerChild function with its computed ID. The parent can then keep track of these IDs however it needs to.
Your syntax is kind of confusing. Let's convert the pseudo-example to an actual working ReactJS code sample.
Let's say you have a child(s) component(s):
class Thing1 extends React.Component {
constructor(props) {
super(props);
this.id = 'Thing1IDString';
}
render(){
return (
<p>Child component</p>
);
}
}
If you want to access Thing1's id property from your parent component, there are several ways how to do communicate between components.
It depends what you want to achieve. If you just want to access a child component property, you may use refs:
class Parent extends React.Component {
doSomething() {
// Access anything you need from `this.refs.thing1`
const thing1ID = this.refs.thing1.id;
}
render() {
return (
<div>
<button onClick={this.doSomething.bind(this)}>Get ID</button>
<Thing1 ref="thing1" />
</p>
);
}
}
I've touched above problem here: https://github.com/facebook/react/issues/15320
There are 3 methods you can use to achieve Parent child data transfer (context Api, mutatiin, Reac.Children deep traversal).

React Nested loops

I have some grouped data that I would like to render like such:
RowGroupTableHeader
RowGroup1
RowGroup1-row1
RowGroup1-row2
RowGroup2
RowGroup2-row1
RowGroup2-row2
RowGroup3-row3
Normally, I would do a loop through the groups, and within each loop, I would have an internal loop to go through each of the rows.
However, because I'm working with accessibility, div structures are very strict. eg. you cannot just throw in a surrounding container around div[role=rowgroup]. i.e. each rowgroup needs to be at the same level.
As such, I cannot use the usual Array.map() nested within each other because after the first iteration, I am expected to close the return() and cannot start a new Array.map().
Anyone got any ideas how I can achieve this?
Is there a wrapper component out there that can render its contents without the wrapper? eg. {content}?
Thanks.
John.
I'm not sure I completely understand the question. But your last sentence leads me to believe that you want to to write a React class that renders its child directly, without wrapping it in an outer element like div? If so, that is possible, but ONLY if you pass a SINGLE valid React component in as a child.
The following should work:
class Foo extends React.Component {
constructor(props){
super(props);
}
render() {
if(React.Children.count(this.props.children) === 1){
// If there is only one child, return it directly, unwrapped
return React.Children.only(this.props.children);
} else {
// Otherwise, return all children wrapped in div
return <div className="foo">{this.props.children}</div>
}
}
}
...
// Later on...
<Foo><div>Hello, world!</div></Foo>
// Would render simply as
// <div>
// Hello, world!
// </div>
<Foo><div>Bar</div><div>Baz</div></Foo>
// Would render as
// <div class="foo">
// <div>Bar</div>
// <div>Baz</div>
// </div>

How to access data in a child react component from a parent react component

I'm new to ReactJS. I want to be able to set some properties of a React component and then be able to access it from a parent React component. But I'm not entirely sure how to do this. For example, consider the following two classes:
export default class SubWindow extends React.Component {
click(event)
{
this.myCollection.push({name:'receiptNum',value:$(event.currentTarget).html()});
}
render()
{
return (
<ul>
<li onClick={this.click.bind(this)}>0</li>
<li onClick={this.click.bind(this)}>1</li>
<li onClick={this.click.bind(this)}>2</li>
<li onClick={this.click.bind(this)}>3</li>
</ul>
);
}
}
export default class MainWindow extends React.Component {
click(event)
{
console.log(SubWindow.myCollection);
}
render()
{
const SubWindow = require('./SubWindow').default;
return (
<SubWindow />
<button onClick={this.click}>Log subwindow array</button>
);
}
}
Basically, I want the SubWindow to have a property called myCollection which is just an array of JSON objects. myCollection gets populated by each click on the list item.
Later, I want to be able to console.log(SubWindow.myCollection) when I press on a button in the parent window. My question how do I access the SubWindow.myCollection from a parent react component?
I would recommend you to solve this problem by using callback. MainWindow is creating SubWindow, and you can give here a callback function as a property. For example:
<SubWindow onClick={this.onSubwindowClick} />
Now in your SubWindow class, just call this callback inside your click function:
click(event)
{
this.myCollection.push({name:'receiptNum',value:$(event.currentTarget).html()});
this.props.onClick(/* data you want to pass to the parent */);
}
Also you have to define onSubwindowClick in the class MainWindow. This function should receive any data you wish from child class - the data which you pass from child where I put comment /* data you want to pass to the parent */.
Also, don't forget to bind this to that onSubwindowClick function. This is usually done in constructor of the class, which I suggest you to create for each component.
You can find good example about "passing data to parent" on React's pages. You can take a look at the article Thinking in React, particularly section "Step 5: Add inverse data flow".
just pass a function to you child component, and the function is bind to the parent component's 'this', actually you just created a closure.
then in your parent component, the function's args are passed in your child component, meanwhile your parent component's scope has access to the args, so in the parent scope you can get access to the data in the child scope.

Resources