I'm struggling to properly use .map() on a state object's array to display elements. Here's my code
class EventListFront extends Component {
state = {
currentEvent: new event(),
events: new eventList(),
}
...
<View>
{this.state.events.events.map(event => {
<View key={event.id}>{event.title}</View>})}
</View>
My Event structure is as follows:
class event {
constructor(
id = -1,
name = 'UNSET',
description = 'UNSET',
location = 'UNSET',
...
) {}
(eventList is an array of events, and it redundantly has an identifier 'events')
With this implementation, i have nothing showing when I add to the array. I've tried using a FlatList, but that requires keys/IDs for each element. I'm not sure how to
update the page with the new list when a new event is added
get the elements of events to be displayed in elements using .map()
use the generated ID so that it may properly render with a FlatList instead.
it seems there are several problems in your code :
as Tom Slutsky commented, you need to return the component in the callback that you pass to your map function. There are several cases for the return value in arrow functions depending on how you write them. Syntax of arrow functions
It seems that you are using always the same id in your events (-1), but when rendering a list of items, React requires to have unique keys for each component. If you update your state with set setState pushing a new item in your events array, your component will be re-rendered and updated.
Regarding the use of Flatlist, they have performance optimizations that are useful if you handle long lists of components. If you use map, all your list will be rendered even though components are not visible on the screen. With Flatlist, only components that are visible are rendered.
Related
I created a Linked-List data structure in my react-native app that I want to move between screens and then pick a node based on a checkerbox selection menu.
I understand that I can move the list using react-native-navigation, so now I would like to display the list with a checkerbox list to select multiple nodes and perform actions on them. The problem I see is that checkerbox lists use defined const arrays of items that are then listed.
The whole reason I went with linked-lists is that I need the list to dynamically update. (It may be more beneficial to use an array of large size instead, but each element within the node is somewhat large and I am unsure what effect a large array would have.)
Is there a way to input a linked list into a checkerbox list or would I have to create an array?
How would I go about either option if they need to dynamically update?
It may be more beneficial to use an array of large size instead, but each element within the node is somewhat large and I am unsure what effect a large array would have.
JavaScript arrays only hold references to the objects they store since it is a dynamic scripting language supporting prototype based object construction. Because of this, each element's size will not affect the array performance.
Another possibility is to extend the built-in Array class in your Linked-List data structure to ensure compatibility.
Your decision on using an Array or a Linked-List should be based on the List operations your app uses the most. There are a lot of articles about that.
Is there a way to input a linked list into a checkerbox list or would
I have to create an array?
How would I go about either option if they need to dynamically update?
There are some git repositories that add support for what you want to achieve (here is an example, feel free to explore npm for more).
Another possibility, if you want your items to dynamically update, will be to encapsulate them in a React.Component for rendering:
// YourNode.js
import React, { Component } from 'react';
import { CheckBox } from 'react-native';
export default class YourNode extends Component {
constructor(props) {
super(props);
this.state = {
checked: false,
};
}
selectItem = () => {
const { onPress } = this.props;
// Update the state
this.setState({ checked: !this.state.checked });
onPress();
};
render() {
const { node } = this.props;
// You may want to render more things like `Text` or `View` components based on
// the node's content
return (
<CheckBox
value={this.state.checked}
onValueChange={this.selectItem}
/>
);
}
}
import YourNode from './YourNode';
// YourScreen.js
render() {
//...
// This will render a `YourNode` component for each one of your nodes.
{yourList.map((item, index)) => {
return (<YourNode node={item} onPress{() => this.selectedNodes.push(item)}>)
}
}
}
We have a crazy DOM hierarchy, and we've been passing JSX in props rather than embedding children. We want the base class to manage which documents of children are shown, and which children are docked or affixed to the top of their associated document's window.
List (crazy physics writes inline styles to base class wrappers)
Custom Form (passes rows of JSX to Base class)
Base Class (connects to list)
Custom Form (passes rows of JSX to base class)
Base class (connects to list)
The problem is that we're passing deeply nested JSX, and state management / accessing refs in the form is a nightmare.
I don't want to re-declare every row each time, because those rows have additional state attached to them in the Base Class, and the Base Class needs to know which rows actually changed. This is pretty easy if I don't redeclare the rows.
I don't know how to actually deal with rows of JSX in Custom Form.
Refs can only be appended in a subroutine of render(). What if CustomForm wants to measure a JSX element or write inline CSS? How could that JSX element exist in CustomForm.state, but also have a ref? I could cloneElement and keep a virtual DOM (with refs) inside of CustomForm, or depend on the base class to feed the deeply-nested, mounted ref back.
I believe it's bad practice to write component state from existing state. If CustomForm state changes, and I want to change which rows are passed to BaseClass, I have to throttle with shouldComponentUpdate, re-declare that stage document (maintaining row object references), then call setState on the overarching collection. this.state.stages.content[3].jsx is the only thing that changed, but I have to iterate through every row in every stage document in BaseClass when it sees that props.stages changed.
Is there some trick to dealing with collections of JSX? Am I doing something wrong? This all seems overly-complicated, and I would rather not worsen the problem by following some anti-pattern.
Custom Form:
render () {
return <BaseClass stages={this.stages()}/>
}
stages () {
if (!this._stages) this._stages = { title: this.title(), content: this.content() };
return this._stages;
}
title () {
return [{
canBeDocked: false,
jsx: (
<div>A title document row</div>
)
}
}
content () {
return [{
canBeDocked: false,
jsx: (
<div>Hello World</div>
)
}, {
canBeDocked: true,
jsx: (
<div>Yay</div>
)
}
}
What I usually do is just connect the lower level components via Redux. This helps with not passing the state in huge chunks from the top-most component.
A great video course by one of the React creators, Dan Abramov: Getting started with Redux
Absolutely agree with #t1gor. The answer for us was to use REDUX. It changed the entire game for us. Suddenly a button that is nested 10 levels deep (that is, inside a main view, header, header-container, left side grid, etc, etc, deeper and deeper) into purely custom components, has a chance to grab state whenever it needs.
Instead of...
Parent (pass down state) - owns state vars
Child (will pass down again) - parent has state vars
Grandchild (will pass down a third time) - grandparent has state vars
Great Grandchild (needs that state var) - great grandparent has state vars
You can do...
Parent (no passing) - reads global state vars
Child
Grandchild
Great Grandchild - also reads same global level state vars without being passed...
Usually the code looks something like this...
'use strict'
//Importation of Connection Tools & View
import { connect } from 'react-redux';
import AppView from './AppView';
//Mapping -----------------------------------
const mapStateToProps = state => {
return {
someStateVar: state.something.capturedInState,
};
}
const mapDispatchToProps = dispatch => {
return {
customFunctionsYouCreate: () => {
//do something!
//In your view component, access this by calling this.props.customFunctionsYouCreate
},
};
}
//Send Mappings to View...
export default connect(mapStateToProps, mapDispatchToProps)(AppView);
Long story short, you can keep all global app state level items in something called a store and whenever even the tiniest component needs something from app state, it can get it as the view is being built instead of passing.
The issue is having content as follows, and for some reason not being able to effectively persist the child instances that haven't changed (without re-writing the entire templateForChild).
constructor (props) {
super(props);
// --- can't include refs --->
// --- not subroutine of render --->
this.state = {
templateForChild: [
<SomeComponentInstance className='hello' />,
<AnotherComponentInstance className='world' />,
],
};
}
componentDidMount () {
this.setState({
templateForChild: [ <div className='sometimes' /> ],
}); // no refs for additional managing in this class
}
render () {
return ( <OtherManagerComponent content={this.state.templateForChild} /> );
}
I believe the answer could be to include a ref callback function, rather than a string, as mentioned by Dan Abramov, though I'm not yet sure if React does still throw a warning. This would ensure that both CustomForm and BaseClass are assigned the same ref instance (when props.ref callback is executed)
The answer is to probably use a key or createFragment. An unrelated article that addresses a re-mounting problem. Not sure if the fragment still includes the same instances, but the article does read that way. This is likely a purpose of key, as opposed to ref, which is for finding a DOM node (albeit findDOMNode(ref) if !(ref instanceof HTMLElement).
I'm creating a react file tree, and I have the tree setup as a React component. The tree can take a contents prop that is an array of either strings, or other <Tree /> components (this enables the nested file structure UI). These tree components can be nested indefinitely.
I need to register a click event on the children of the nested tree components, but I'm having trouble getting it to work beyond the first level of nesting. A simplified example of what I'm dealing with:
//In App - the top level component
const App = React.createClass({
_handleChildClick () {
console.log("this is where all child clicks should be handled");
},
render () {
return (
<Tree
handleChildClick={this._handleChildClick}
contents={[
<Tree />
]}
/>
);
}
});
//And in the tree component
<div onClick={this.props.handleChildClick}></div>
If you want to see more detail - here's the github repo.
I tried researching this question and saw people using {...this.props} but I'm not sure if that applies to my scenario - if it does, I couldn't get it to work.
Thanks for any help on this.
The reason why the click handling does not work beyond the first level is because your second level Tree component (the one inside the contents array) does not get the appropriate prop handleChildClick passed in. (BTW I think the convention is to call the prop onChildClick while the handler function is called handleChildClick - but I digress.)
Do I understand correctly that you actually want to inform each layer from the clicked component up to the top? For this to happen, you need to extend the props of the tree component that is inside the contents array - it needs to receive the click handler of its parent component. Of course, you cannot write this down statically, so it needs to be done dynamically:
Your Tree component, before actually rendering its children, should extend each of them with the component's click handler, which can be done using the function React.cloneElement (see API documentation and a more detailed discussion). Directly applying this to your component makes things a bit messy, because you are passing the component's children in a prop, so you need to figure out which prop to modify. A bit of a different layout would help you quite a lot here:
<Tree handleChildClick={this._handleChildClick}>
<Tree />
</Tree>
looks nicer IMHO and makes the structure much clearer. You can access the inner components via this.props.children, and cloneElement will be much simpler to use.
So, in your Tree component, you could have a render method like this:
render () {
const newChildren = this.props.children.map(child =>
React.cloneElement(child, {onChildClick: this._handleChildClick}));
return (
<div>{newChildren}</div>
);
}
Please note that this code will not work if you have a mixture of strings and Tree components, therefore my third and last suggestion would be to wrap those strings into a very thin component to allow for easier handling. Alternatively, you can of course do a type comparison inside the map.
I am using ReactJS, and I was wondering whether it is possible to highlight an element in the DOM when its value has been changed.
I have a list of elements whose values update periodically. While I have no problem animating in the DOM new items coming in to the list or items leaving the list using React's animation library interface, I am struggling to figure out how to detect the actual value change of the existing elements in the list.
Any ideas on how to do so? Thank you!
I had the same problem, then I decided to create a generic small module to handle it everywhere, that you can install from here
https://www.npmjs.com/package/react-change-highlight
using
yarn add react-change-highlight
what you can do to use it is to wrap you part that you want to highlight inside the component itself as follow
import ChangeHighlight from 'react-change-highlight';
export default () => {
const [count, setCount] = useState(0);
return (
<ChangeHighlight>
<div ref={React.createRef()}>{count}</div>
</ChangeHighlight>
);
}
and you will find the highlight on change as in the example below
I hope you find this useful
If your new values are coming down as new props, then you can use the componentWillReceiveProps lifecycle method on the component. It is called just before the component is updated.
Within that method, you can compare the new values (passed as the argument to the method) and the old values (available as this.props).
For instance:
componentWillReceiveProps: function(newProps) {
if(this.props.query != newProps.query) {
//handle change of query prop
//may include calls to this.setState()
}
}
Assuming that your elements are input elements, you need to use the onChange event listener. If you want to highlight that element, I would suggest having a CSS class that highlights the element and then conditionally applying it in render() based on state (or props if a child).
So for instance, you can have one component, add a handler function and the appropriate listener in the render():
handleChange: function(evt) {
//input was changed, update component state (or call parent method if child)
this.setState({changed: true});
},
render: function() {
return (<div>
<input type="text"
className={this.state.changed ? 'highlight' : ''}
onChange={this.handleChange}
/>
</div>);
}
That should set you in the right direction.
I am currently going through the documentation of React.js and have a question about this.props, which according to the docs should be considered immutable and only pushed downwards down the ownership tree since bubbling custom events upwards is discouraged.
Say that I have a UI where the state of a component in the header of the page should be shared with another component that is nested somewhere in the body of the page, let's take a simple checkbox that represents some specific state that will influence the visibility of some nested spans or divs.
The only I way I currently see this achieved is by creating a state property that is pushed downwards from the top element to the child elements.
The two related questions I have with this is:
Does this mean that I should create one component that owns the entire page? (Is rendering the entire page with a single owner component an acceptable thing to do? I understand the concepts of Virtual DOM and diffing so I assume it's not a problem, still I'd like some confirmation in case I miss out on something relevant);
Is it ok to change the property on this.props when clicking the checkbox, in order to re-render the other components on the page? This doesn't make the props immutable (perhaps they mean just that setting new props on this.props down the chain is not accepted to avoid an untransparent stack trace in case of bugs, but changing the value of a state property is?).
Some confirmation would be appreciated.
Thanks.
1) It is fine to have one parent for the whole page, but is not always necessary. It depends on if it is necessary to share the state between components.
2) You never want to alter props via this.props.someValue = newValue. If you need to modify the parent state from a child component, it should always be done with a callback. The example below shows how to share the checkbox state between multiple components using the callback function handleClick to modify the state of is_checked.
JSFiddle of example: https://jsfiddle.net/mark1z/o7noph4y/
var Parent = React.createClass({
getInitialState: function(){
return ({is_checked: 0})
},
handleClick: function(){
this.setState({is_checked: !this.state.is_checked})
},
render: function(){
return (
<div>
<CheckBox is_checked={this.state.is_checked} handleClick={this.handleClick}/>
<OtherComponent is_checked={this.state.is_checked} />
</div>
);
}
});
var CheckBox = React.createClass({
render: function() {
return (
<input type="checkbox" onChange={this.props.handleClick}> Show other component </input>
);
}
});
var OtherComponent = React.createClass({
render: function() {
return (
<div style={{marginTop: 20}}>
{this.props.is_checked ? 'The checkbox is ticked' : 'The checkbox is not ticked'}
</div>
);
}
});
React.render(<Parent />, document.getElementById('container'));
I guess having one master component isn't an issue. The docs suggest that you find the topmost component that can supply it's children with the needed data - and this could easily be the toplevel master component. As I understand this you would have a master component for your main page - that should be the only one that uses state, the children just render what they are given in props. So no, props should not be altered by a child that doesn't own the data, it is the topmost components prerogative to do so. Let's say you have another widget on the page that only cares for a distinct set of data you would make this the root of another tree that fetches data and sets it's state and the props of it's children.
Here is a crappy graph for this situation:
App -(props)-> ItemList -(props)-> Item -(props)-> Photo
+ + |
+ ++++++++++ |----(props)-> LikeButton
+ + |
(fetch) + |
+ + * ---(props)-> Description
++(setState)++
Widget -(props)-> Whether
However it gets more interesting when facebook's graphql is finalized and every component can declare the needed data on it's own, I'm looking forward to it. But until then the toplevel component has to know which data every child needs and all the parent nodes need to hand this data down.