I need to show data depending on an active device (which can be iphone or ipad). I have 3 components: App, DeviceCheck and Collection. Device check is a component with 2 buttons, Iphone and iPad. In a Collection component I'm parsing data from an external file, for example something like:
const applications = [
{
device: 'iPhone',
icon: icon,
title: 'title',
descr: 'descr',
category: 'Games',
link: 'link-to-appstore',
price: 0.99,
purchases: true
},
{
device: 'iPad',
icon: icon2,
title: 'title2',
descr: 'descr2',
category: 'Games',
link: 'link',
price: 1.99,
purchases: false,
}
]
The App.js structure is:
<DeviceCheck />
<Collection />
How do I show the iPhone or iPad data in Collection component, depending on which button was clicked in DeviceCheck component?
Create a component which passes a callback to the device check.
Use this callback to update the state of the container from the device check.
Use the state in the container to set the props of the collection.
This is very common in React and is the basis of how the compositional pattern works. If you need to share data between two components just put them in a container and lift the state to the parent component. These components are usually called containers and there is a bunch of documentation on it.
This is a good starting point: https://reactjs.org/docs/lifting-state-up.html
A rough layout would be something like this.
class Container extends React.Component {
constructor(props) {
super(props);
// Don't forget to bind the handler to the correct context
this.changeDevice = this.changeDevice.bind(this);
}
changeDevice(device) {
this.setState({device: device});
}
render() {
return (
<DeviceCheck btnCb={this.changeDevice} />
<Collection device={this.state.device} />
)
}
}
Maintain a variable in the state of App.js called selectedDevice.
In the click handler of the buttons call setState() to modify selectedDevice in state.
Use selectedDevice to show the corresponding data in <Collection />
It's a little broad your question, but you can hold a state object with the selected device and use setState on <DeviceCheck /> button click to set the selected one. and <Collection {...selectedDevice} /> will use the state object as input.
In the App component you can have deviceType as the state and a function which sets the deviceType based on the parameter and this function should be passed as props to the DeviceCheck component. Upon clicking button the in the DeviceCheck button, you need to invoke the prop function with corresponding button type as parameter. The deviceType state should be passed onto the Collection component as prop and based on the prop value, the Collection component can render the respective Device data. Hope this helps!
Related
let's imagine data that would look like that :
{
{
title: "great track",
tags: ["techno"]
},
{
title: "superb track",
tags: ["house", "90s"]
},
...
}
I render that in an html table, I have a component for the whole table, and a sub component for the tr (aka song title and tracks). so on each line I want to allow the users to be able to access a popup in which they can choose one or more tags for a song. I did it using reactstrap, it works ok.
I'm just a little disappointed by performance, it's quite ok, once it's built, but I saw how much longer it was to load when I added my modal on each line. So my first reflex, was to built only one modal in the parent component, and then use it from the sub component, and then I read articles on how, "one should not use the parent instance because it's bad"(tm).
I understand the point about dataflow, but in my example, having a modal waiting on each line while I'm sure I will never have two at the same time on screen feels like a waste of ressources.
Can anyone point me to an elegant way of building that kind of feature, in this particular context ?
Lifting state up to the parent component is a common practice in react, you can read articles in official documentation https://reactjs.org/docs/lifting-state-up.html
But there is one problem, when you use setState in your parent component, your songs table will render again and again, so you should care about it. One of the way is creating PureComponent for Songs table(if there is no changing in props, this component will not rerender)
I think, the code below is one of the way;
class Parent extends React.Component{
state={
tags: [],
songs: {
title: "great track",
tags: ["techno"]
},
{
title: "superb track",
tags: ["house", "90s"]
}
}
handlePopup(data){
this.setState({tags: data});
}
render(){
const {tags, songs} = this.state;
cons isShowModal = tags && tags.length
return (
<div>
{isShowModal && <Modal data={tags} />}
<SongsList data={songs} />
</div>
)
}
}
class Parent extends React.PureComponent{
render(){
const {data} = this.props;
return (
<table><tbody>...render songs data</tbody></table>
)
}
}
Of course using modal in child rows is a waste of resources. you need to add a modal to parent and use props to show/hide it. also, you should use the key prop for your list items.
By default, when recursing on the children of a DOM node, React just iterates over both lists of children at the same time and generates a mutation whenever there’s a difference.
but when children have keys, React uses the key to match children in the original tree with children in the subsequent tree.
it's good for performance.
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
We are in the process of implementing performance optimizations in our react/redux application. Part of those optimizations included introducing reselect. This worked nice for data that is derived directly from the state. but what about data that is derived from other props?
Example:
We have 3 components Feed FeedItem and Contact (Contact is a component for displaying a users contact information).
a FeedItem gets an object that represents an item in the feed, one of the properties of a feed item is an actor object. This object is like a user but a bit different (this sucks but can't be changed). This means that if I want to render a Contact for this actor I need to create a new object that maps the properties from an actor to a user. Creating a new object on every render is a performance anti pattern because we are using shallow equality checks.
e.g code:
<Contact
user={{
photoUrl: actor.photo.smallPhotoUrl,
Id: actor.id,
Name: actor.name,
}}
</Contact>
Is there a pattern for solving this? reselect only supports derived data from redux state, this is basically derived data from props.
You can pass whatever you want to reselect's selector methods. It doesn't have to be state and props. That just happens to be it's most common use case. You can call one if it's generated selectors with any number of arguments.
Here's one way you could use it:
function convertActorToContactUser(actor) {
return {
photoUrl: actor.photo.smallPhotoUrl,
Id: actor.id,
Name: actor.name,
};
}
class ActorContact extends Component {
constructor(...args) {
super(...args);
this.getUser = createSelector(
() => this.props.actor,
convertActorToContactUser
);
}
render() {
return <Contact user={this.getUser()} />
}
}
I've just started on reactjs and am trying to figure out the right way to design my components. I get the concept of components, props, states but struggling a bit with the right way to design for a hierarchy.
I have a page that's driven by a big object array. Say
objArr = {
param1 : value1,
param2: value2,
dataArray: [
{ key: "keyvalue", a: "a", b: "b", c: "c" },
...
]
}
The entire page builds off of this. The page builds a series of UI components corresponding to the dataArray.
Now each time some of the icons are clicked in the UI, I want some changes, and the icons correspond to a value on this dataArray. What's a good way to ensure the dataArray values are changed as the UI is acted on? and vice versa, the UI changes as values are changed on the dataArray.
I've read "make components as stateless as possible," fine - then how do I do this handling at the parent component level and have it flow down?
I don't need code examples. Just a few pointers of the way to architect my ReactJS code would be great, I will figure out the code.
Thank you
What you can do is bind a method on the Parent component and pass it down to the child container like so:
// Our 'pages' that we want to be able to interact with
const ComponentWIthButtons = ({handleButtonClick, buttons}) => (
<div>
<p>Hey, click one of these</p>
{buttons.map(button => (
<div onClick={()=>handleButtonClick(button.name)}>
{button.text}
</div>
))}
</div>
)
// The Parent we want to interact with
class App extends React.Component {
constructor(props){
super(props)
this.state = {
buttons: [
{
name: 'a',
text: 'Awesome button'
},
{
name: 'b',
text: 'Beautiful button'
},
{
name: 'c',
text: 'Cool button'
}
]
}
}
// This is what you want to do with the information passed
handleButtonClick = (name) => {
// this could be updating this components' state, which
// would rerender the display component because we pass it
// our buttons from our state object
console.log(name)
};
render(){
return <div>
<h2>Below are some buttons!</h2>
// If you don't use the syntax above, you will run into
// errors trying to get a reference to this container through
// `this`. You can do this.handleButtonClick.bind(this)
// to get around that
<ComponentWIthButtons handleButtonClick={this.handleButtonClick} buttons={this.state.buttons} />
</div>
}
}
Say I have the following component structure:
App
DivContainer1
Child1
Button1
Child2
Button2
DivContainer2
AComponentHere
When I press Button1, I want to replace the contents of DivContainer2 with another component, e.g. replace AComponentHere with AnotherComponent that will render data based on which Child's button was clicked.
Should there be an AppStore and AppAction that tells it to change to another component, or should I pass down props that change the state here? Basically, I would like to know if Stores are used for only holding data state, or also holding ui state.
You can definitely have a store for UI state. In this case, it sounds like you are looking for a way to implement navigation. If you don't want to use something large like react-router, I would just create a simple UIStore or NavigationStore with an identifier indicating which component is currently shown:
// store setup...
var flags = {
currentPage: PAGES.home // page names should be in shared file somewhere...
};
// rest of store impl to manage access to these UI flags...
To change the contents, fire off an action from the onClick event (for example) that updates the store property to match the component you want to show.
handleClick: function(e) {
// this should trigger store to change the currentPage
AppActions.navigateTo(PAGES.about);
}
Then, in render(), you just have a conditional or switch statement that renders the correct output for each given value:
//...
render: function() {
return (
<div class="DivContainer2">
{switch (this.state.currentPage) {
case PAGES.home:
<HomePage />
break;
case PAGES.about:
<AboutPage someAttr={true} />
break;
default:
<NotFoundPage />
}}
</div>
);
}