Recently, I have been searching through to understand the development behind a live search input, the content being an array. In my case, the array is being used to create a file tree. All the code is here: https://codesandbox.io/s/815p3k3vkj
After some investigation and looking through some working examples, the solution seemed simple, but then I understood that I was still confused about it's creation. So, I defined the initial state, but I'm confused of what to do next, to connect the search with the array.
So, I started to do something like this:
export class SearchEngine extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ""
};
this.inputChange = this.inputChange.bind(this);
}
inputChange(e) {
const content = e.target.value;
this.props.onChange();
}
static defaultProps = {
onChange: (search) => {
}
}
render() {
return (
<input placeholder="Search the tree..." onChange={this.inputChange}/>
);
}
}
export default SearchEngine;
And here is the array and <FileTree>:
let data = [
{
type: "directory",
name: ".",
contents: [
{
type: "directory",
name: "./bin",
contents: [{ type: "file", name: "./bin/greet" }]
},
{
type: "directory",
name: "./lib",
contents: [{ type: "file", name: "./lib/greeting.rb" }]
},
{
type: "directory",
name: "./spec",
contents: [
{ type: "file", name: "./spec/01_greeting_spec.rb" },
{ type: "file", name: "./spec/02_cli_spec.rb" },
{ type: "file", name: "./spec/spec_helper.rb" }
]
},
{ type: "file", name: "./CONTRIBUTING.md" },
{ type: "file", name: "./Gemfile" },
{ type: "file", name: "./Gemfile.lock" },
{ type: "file", name: "./LICENSE.md" },
{ type: "file", name: "./README.md" }
]
}
];
export class FileTree extends React.Component {
constructor(props) {
super(props);
this.state = {
activeNode: null
};
this.setActiveNode = this.setActiveNode.bind(this);
}
setActiveNode(name) {
this.setState({ activeNode: name });
this.props.liftStateUp(name);
}
render() {
return (
<div className="padd_top">
{renderTree(
this.props.root || root,
this.setActiveNode,
this.state.activeNode
)}
</div>
);
}
}
export default FileTree;
I appreciate all the clarity given in this matter and I want to thank in advance all the help you can provide. I'm a ReactJS newbie and in the middle of new understandings.
Thank you.
Your problem is that you have no defined props.onChange function inside SearchEngine class. If your component depends on prop.onChange being a function and you are calling it directly you have to setup required prop
import PropTypes from 'prop-types'
class SearchEngine extends React.Component {
static propTypes = {
onChange: PropTypes.func.isRequired
}
}
alternatively you can guard your component with
class SearchEngine extends React.Component {
static defaultProps = {
onChange: () => {}
}
}
which will in fact do nothing in case prop is not passed but it will not crash. From your example is not clear what onChange should do but you are not passing it from index.js where you are rendering SearchEngine
EDIT: working solution https://y7p6zlmpyx.codesandbox.io/
Related
I have an object of all apps:
export const allApps: appInterface[] = [
{
id: uuidv4(),
name: "Minesweeper",
icon: minesweeperIcon,
disabled: false,
},
];
I want to add component property to each object like this:
export const allApps: appInterface[] = [
{
id: uuidv4(),
name: "Minesweeper",
icon: minesweeperIcon,
disabled: false,
component: Minesweeper, //It is imported as import Minesweeper from ../Components/Minesweeper";
},
];
And then I want to display all components in App.js:
allApps.forEach((app)=>{
<div>{app.component}<div> // for eg: <div><Minesweeper/></div>
});
Is this possible to do?
Try as below code it should work.
allApps.map((app)=>{
const { component: Component } = app;
return <Component />;
});
I have components like these
type TestComponentProps = {
title: string;
}
const TestComponent: React.FC<TestComponentProps> = ({
title,
}) => {
return <div>TestComponent: {title}</div>;
};
type TestComponent2Props = {
body: string;
}
const TestComponent2: React.FC<TestComponent2Props> = ({ body }) => {
return <div>TestComponent2: {body}</div>;
};
I would need an interface that would allow me to configure which component to render and get the props of that particular component
const dataToRender:Array<{
component: TestComponent | TestComponent2,
data: propsOf<component>
}> = [
{
component: TestComponent,
data: { title: '123' }
},
{
component: TestComponent2,
data: { body: 'lorem ipsum' }
}
];
Ideally I'd need to get the props of the particular component in a way "I want to render this component and I can only accept the correct props based on the props of that component"
You can do this, but not with plain typesciprt type annotations. In vue js for particular, for better typing you need to use defineComponent wrapper, that just return it argument (id) but with types.
In you case you can use this function.
function defineComponent<TProps, TModal extends React.ComponentType<TProps>>(x: {
component: TModal & React.ComponentType<TProps>
data: TProps
}) {
return x
}
And use it like so:
const dataToRender = [
defineComponent({
component: TestComponent,
data: { title: "123" },
}),
defineComponent({
component: TestComponent2,
data: { title: "lorem ipsum" }, // error here
}),
] as const
I have a parent component with 2 child components that both inherit from the same base component. (This parent component is being created and used in a Vue Storybook). Both SiblingAComponent and SiblingBComponent inherit the same BaseComponent, and instantiate the same inherited data classInstance, which is a vanilla JS class instance from another library. I am trying to access this classInstance from the parent component to pass as data into the second sibling component (in this case, from SiblingAComponent to SiblingBComponent), by using an reference siblingARef. However, I get this error from the storybook compiler:
too much recursion
isArguments#http://localhost:6006/vendors~main.9107ef8d0bc0558399e1.bundle.js:49010:16
keys#http://localhost:6006/vendors~main.9107ef8d0bc0558399e1.bundle.js:49073:28
_traverse#http://localhost:6006/vendors~main.9107ef8d0bc0558399e1.bundle.js:119972:19
_traverse#http://localhost:6006/vendors~main.9107ef8d0bc0558399e1.bundle.js:119974:28
ParentComponent Story:
storiesOf("ParentComponent Story", module)
.addDecorator(
withKnobs({
escapeHTML: false
})
)
.add("Passing data from A to B", () => ({
name: 'ParentComponent',
components: {
SiblingAComponent,
SiblingBComponent,
},
data() {
return {
siblingAData: [....], // array of objects
siblingAOptions: {
axes: {},
height: "50px",
},
siblingBData: [...], // array of objects
siblingBOptions: null,
}
},
mounted() {
const siblingAInstance = this.$refs.siblingARef.classInstance;
const newOptions = {
legend: {
external: {
reference: siblingAInstance,
},
},
};
// this line is where I am getting an error
this.siblingBOptions = legendExternal;
},
template: `
<SiblingAComponent ref="siblingARef" :data="siblingAData" :options="siblingAOptions"/>
<SiblingBComponent v-if="siblingBData" :data="siblingBData" :options="siblingBOptions"/>
`,
}));
SiblingAComponent:
<template>
<div class="sibling-a-component"></div>
</template>
<script>
import { ComponentA } from '#libraryexample/components';
import BaseComponent from './base-component.vue';
export default {
name: 'SiblingAComponent',
extends: BaseComponent,
mounted() {
this.classInstance = new ComponentA(this.$el, {
data: this.data,
options: this.options,
});
},
};
</script>
SiblingBComponent:
<template>
<div class="sibling-b-component"></div>
</template>
<script>
import { ComponentB } from '#libraryexample/components';
import BaseComponent from './base-component.vue';
export default {
name: 'SiblingBComponent',
extends: BaseComponent,
mounted() {
this.classInstance = new ComponentB(this.$el, {
data: this.data,
options: this.options,
});
},
};
</script>
BaseComponent:
<script>
export default {
name: 'BaseComponent',
data() {
return {
classInstance: null,
};
},
props: {
data: { type: [Object, Array], required: true },
options: { type: Object, required: true },
},
};
</script>
Coming from the Angular and React worlds, using a reference to access a Vanilla class instance from another Component is nothing new, even if it's unconventional. I am new to Vue, so I am wondering why would trying to access a class instance fail (works fine for primitive data types) and give me such a weird error? Where is the recursion occurring?
I'm using a reactstrap dashboard theme
The side menus are populated by an array of menu objects.
I wish to:
populate the array from XHR
set the raw data blob as a global variable for the rest of the app to use.
Here is what I'm trying ...
class ClusterItems extends Component {
constructor (props) {
super(props)
this.state = {
apiurl: ' ',
clusterItems: [
{
name: 'Clusters',
title: true,
wrapper: { element: '', attributes: {} },
class: ''
},
{
divider: true
}
]
}
}
componentDidMount () {
const apiurl = 'http://validapiurl/clusters'
axios.get(apiurl).then(results => (
this.state.clusterItems.push({
icon: 'icon-layers',
name: item.clusterName,
url: '/clusters' + item.clusterName + '/instances'
})
))
}
}
The part that I'm missing is how to export this.state.clusterItems as an ARRAY and how to do the global export of the data blob clusters.
Here is my mutation class definition that I am using for creating fields. For update I am using another class which gives me updated custom field only which is also described below.
This is for create mutation.
import Relay from "react-relay";
class CustomFieldCreateMutation extends Relay.Mutation {
constructor(props) {
super(props);
}
getMutation() {
return Relay.QL `
mutation customField { createField }
`;
}
getVariables() {
return {
customFieldDefinition: {
name: this.props.customFieldName,
schema: {
type: this.props.customFieldType,
format: this.props.customFieldFormat,
title: this.props.title,
allowedOperations: this.props.allowedOperations,
allowedValues: this.props.allowedValues
},
associatedEntityTypes: this.props.associatedEntityTypes
}
};
}
getFatQuery() {
return Relay.QL `
fragment on CreateCommon_CustomFieldDefinitionPayload {
customFieldEdge,
company{
customFields
}
}
`;
}
getConfigs() {
return [{
type: "RANGE_ADD",
parentName: "company",
parentID: this.props.company.id,
connectionName: "createField",
edgeName: "customFieldEdge",
rangeBehaviors: {
"": "append"
}
}];
}
}
export default CustomFieldCreateMutation;
//// This is for update mutation class.
import Relay from "react-relay";
class CustomFieldUpdateMutation extends Relay.Mutation {
constructor(props) {
super(props);
}
getMutation() {
return Relay.QL `
mutation customField { updateField }
`;
}
getVariables() {
return {
commonCustomFieldDefinition: {
id: this.props.customFieldId,
name: this.props.customFieldName,
deleted: !this.props.isActive,
schema: {
type: this.props.customFieldType,
title: this.props.title,
format: this.props.customFieldFormat,
allowedOperations: this.props.allowedOperations,
allowedValues: this.props.allowedValues
},
associatedEntityTypes: this.props.associatedEntityTypes
}
};
}
getFatQuery() {
return Relay.QL `
fragment on UpdateCommon_CustomFieldDefinitionPayload {
customField
}
`;
}
getConfigs() {
return [{
type: "FIELDS_CHANGE",
fieldIDs: {
customField: this.props.customFieldId
}
}];
}
}
export default CustomFieldUpdateMutation;