This question already has answers here:
Using dot notation with functional component
(2 answers)
Closed 1 year ago.
this really sound like a simple thing to know but I couldn't find any articles/resources so asking here.
how can I create components like Form.Input, Form.Select and use as follows -
import Form from '../Form';
...
const MyComponent = (props) => {
...
return (
<Form>
<Form.Input />
<Form.Select />
</Form>
);
}
So I basically want to understand what Form component is like that it is behaving like an object with sub exports like Input as Form.Input and Form.Select.
I've seen such codes at some places. Also we use React.Component, don't we?
I only know to export default and named components so I can create different exports like Input and Select but can't figure out how exporting Form.Input could work ?
The dot notation may make this seem like a subcomponent, but it is just another component attached to the Form component. Take a look at the codesandbox example
Basically, you have a component Form:
export const Form = (props) => {
const Item = ({ children }) => <p> {children} </p>;
Form.Item = Item;
return <div>{props.children}</div>;
};
And you can access this like:
import "./styles.css";
import { Form } from "./Form";
export default function App() {
return (
<Form>
<Form.Item> Test </Form.Item>
<Form.Item> Test 2 </Form.Item>
</Form>
);
}
You can add them to your Form component before exporting :
import React from 'react';
const Form = ({ Input, Select, children }) => (
...
);
const Input = ({ children }) => children;
Form.Input = Input;
const Select = ({ children }) => children;
Card.Select = Select;
export default Form;
More info here if you want :
https://dev.to/shayanypn/buckle-with-react-sub-component-10ll
On file Form.js
export default {
Input,
Select,
}
const Input = (props) => {
// xyz
}
const Select = (props) => {
// xyz
}
Related
I am trying to build an application using "Rick and Morty" REST API. I have created the "card.js" component which fetches data from the API such as characters, name, id, species, and I am mapping on the cards in my "cardList.js" component.
What I am trying to do:
1) I am trying to implement a search bar over the top of the application, which will display a specific card based on what I have searched, let's say i have searched "Rick", so it will display all the cards which has "Rick" in its name.
2) Further, we can also search for anything related to the parameters that i have specified in the card such as gender, type etc.
What I tried already:
1) I created a component called "Search2.js"
2) In this component, I have a "form" which contains the input type, and a button to search the input which the user has entered.
3) In the "cardList.js" component I passed the "Search2.js" component over the "card.js" component so that it stays at the top(search bar and the button)
4) I am mapping over "Search2.js" in my "CardList.js" component to pass the characters as props in my "Search2.js" component, and luckily i am getting all the characters in my console when i am doing console.log({this.props.name}).
The problem:
1) I am getting confused how do i compare individual names(from this.props.name) with the input i have provided in my form in the "Search2.js" component
2) I tried, but all i am getting is my form which is mapped over 493 times(it is the number of characters in the API)
My Card.js component:
import React from "react";
class Card extends React.Component {
render() {
return (
<div>
<h1>ID: {this.props.id}</h1>
<img src={this.props.imgURL} alt="Image failed to load" />
<h1>NAME: {this.props.name}</h1>
<h2>STATUS: {this.props.status}</h2>
<h3>SPECIES: {this.props.species}</h3>
<h3>GENDER: {this.props.props}</h3>
<h4>TYPE: {this.props.type}</h4>
</div>
);
}
}
export default Card;
My CardList.js component:
import React from "react";
import axios from "axios";
import Card from "./Card";
import Search from "./Search";
import Search2 from "./Search2";
class CardList extends React.Component {
state = {
// url: `https://rickandmortyapi.com/api/character/${[...Array(494).keys()]}`,
character: []
};
async componentDidMount() {
//const res = await axios.get(this.state.url);
const ids = Array(493)
.fill(null)
.map((_, idx) => idx + 1)
.join(","); // '1,2,3...,300'
const url = `https://rickandmortyapi.com/api/character/[${ids}]`;
const res = await axios.get(url);
this.setState({
character: res.data
});
}
render() {
return (
<div>
{this.state.character.map(character => (
<Search2 name={character.name} />
))}
{this.state.character.map(character => (
<Card
key={character.id}
imgURL={character.image}
id={character.id}
name={character.name}
status={character.status}
species={character.species}
gender={character.gender}
type={character.type ? character.type : "Unknown"}
/>
))}
</div>
);
}
}
export default CardList;
My Search2.js component :
import React from "react";
class Search2 extends React.Component {
handleSubmit = event => {
event.preventDefault();
const searchValue = event.target.value;
if (searchValue === "{this.props.name}") {
console.log("matched");
} else {
console.log("doesnt match");
}
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<p>
<input type="text" placeholder="Enter character" name="fullName" />
</p>
<p>
<button>Search2</button>
</p>
</form>
);
}
}
export default Search2;
Help me out, please!!
My idea to help you solve this problem goes like this:
render() {
return (
<div>
{
let filter=this.state.character.filter(function(e)
{
//here you can filter out anything you want, for example:
return e.name.includes("Rick") // <- this will check the data you already got from axios //to filter out for every name that contains "Rick', you can then replace "Rick" with your input's value, using State or anything you like
}
filter.map(character => (
<Card
key={character.id}
imgURL={character.image}
id={character.id}
name={character.name}
status={character.status}
species={character.species}
gender={character.gender}
type={character.type ? character.type : "Unknown"}
/>
))}
</div>
);
}
}
this way you will firstly filter out data, then you map this as you did before, but this time it will only show records that get through your filter process
Here you can check the idea behind it JSFIDDLE
Working with an array of mapped items, I am attempting to toggle class in a child component, but state change in the parent component is not passed down to the child component.
I've tried a couple different approaches (using {this.personSelectedHandler} vs. {() => {this.personSelectedHandler()} in the clicked attribute, but neither toggled class successfully. The only class toggling I'm able to do affects ALL array items rendered on the page, so there's clearly something wrong with my binding.
People.js
import React, { Component } from 'react';
import Strapi from 'strapi-sdk-javascript/build/main';
import Person from '../../components/Person/Person';
import classes from './People.module.scss';
const strapi = new Strapi('http://localhost:1337');
class People extends Component {
state = {
associates: [],
show: false
};
async componentDidMount() {
try {
const associates = await strapi.getEntries('associates');
this.setState({ associates });
}
catch (err) {
console.log(err);
}
}
personSelectedHandler = () => {
const currentState = this.state.show;
this.setState({
show: !currentState
});
};
render() {
return (
<div className={classes.People}>
{this.state.associates.map(associate => (
<Person
name={associate.name}
key={associate.id}
clicked={() => this.personSelectedHandler()} />
))}
</div>
);
}
}
export default People;
Person.js
import React from 'react';
import classes from './Person.module.scss';
const baseUrl = 'http://localhost:1337';
const person = (props) => {
let attachedClasses = [classes.Person];
if (props.show) attachedClasses = [classes.Person, classes.Active];
return (
<div className={attachedClasses.join(' ')} onClick={props.clicked}>
<img src={baseUrl + props.photo.url} alt={props.photo.name} />
<p>{props.name}</p>
</div>
);
};
export default person;
(Using React 16.5.0)
First of all, in your People.js component, change your person component to:
<Person
name={associate.name}
key={associate.id}
clicked={this.personSelectedHandler}
show={this.state.show}}/>
You were not passing the prop show and also referring to a method inside the parent class is done this way. What #Shawn suggested, because of which all classes were toggled is happening because of Event bubbling.
In your child component Person.js, if you change your onClick to :
onClick={() => props.clicked()}
The parenthesis after props.clicked executes the function there. So, in your personSelectedHandler function, you either have to use event.preventDefault() in which case, you also have to pass event like this:
onClick={(event) => props.clicked}
and that should solve all your problems.
Here's a minimal sandbox for this solution:
CodeSandBox.io
The main question
I am used to using React with ES6 classes. I am also used to modularizing portions of code into separate functions. I am looking at the following example and trying to figure out how to put the value for onSubmit as a separate function.
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'
let AddTodo = ({ dispatch }) => {
let input
return (
<div>
<form
onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}}
>
<input
ref={node => {
input = node
}}
/>
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
AddTodo = connect()(AddTodo)
export default AddTodo
I have tried something like this:
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'
function handleSubmit(e){
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}
let AddTodo = ({ dispatch }) => {
let input
return (
<div>
<form onSubmit={e => handleSubmit(e)}>
<input ref={node => {input = node }}
/>
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
AddTodo = connect()(AddTodo)
export default AddTodo
But then of course it does not work as it does not recognize the input variable. I could pass the input variable to the function, but this does not seem like the right way to do it.
Question 2:
I am unfamiliar with what the following piece of code is doing:
let AddTodo = ({ dispatch }) => {
Where exactly is it getting dispatch from? Is the value of dispatch being passed into the anonymous function?
Question 3
The same with the following code:
<input ref={node => {input = node }}
Where is the value of node coming from and why is it being stored into the input variable?
Answer to Question 1
AddTodo is a React stateless functional component (SFC). It is also a function. Within the SFC is defined a variable input. In order for the handleSubmit callback to be able to make use of input, it is necessary that input be in the enclosing scope where handleSubmit is defined or input be passed as an argument to handleSubmit.
Thus, the following two implementations achieve the desired behavior:
const AddTodo = ({dispatch}) => {
let input
const handleSubmit = e => {
...
}
return (
...
onSubmit={handleSubmit}
...
)
and
const handleSubmit = (e, input) => {
...
}
const AddTodo = ({dispatch}) => {
let input
return (
...
onSubmit={e => handleSubmit(e, input)}
...
)
I highly recommend reading the following blog post by Kent Dodds, paying particular attention to the use of classes vs function closures.
Classes, Complexity, and Functional Programming
Answer to Question 2
The connect function from react-redux wraps the AddTodo component. The way in which connect is being called (with no second argument, or any arguments in this particular case) means AddTodo will receive a prop named dispatch.
To better understand how react-redux and the connect function it provides work, have a look at the documentation:
https://github.com/reactjs/react-redux/blob/master/docs/api.md
Answer to Question 3
Refs are built into React. A function passed to the ref prop receives the underlying DOM element as an argument. In this case, the function passed to the ref prop stores a reference to the DOM element in the variable input. This allows the DOM element to be accessed and mutated later by the callback passed to onSubmit (i.e. handleSubmit). See the React documentation for more details on refs:
https://reactjs.org/docs/refs-and-the-dom.html
I need to wrap functionality in a, lets say button. However when I call the HOC in the render method of another component I get nothing.
I have this HOC
import React,{Component,PropTypes} from 'react';
export let AddComment = (ComposedComponent) => class AC extends React.Component {
render() {
return (
<div class="something">
Something...
<ComposedComponent {...this.props}/>
</div>
);
}
}
and trying to do this
import {AddComment} from '../comments/add.jsx';
var Review = React.createClass({
render: function(){
return (
<div className="container">
{AddComment(<button>Add Comment</button>,this.props)}
</div>
});
module.exports = Review;
I want AddComment to open a Dialog and submit a comments form when I click the button. I need AddComment to be available other components throughtout the app.
Is the HOC pattern correct? How can I easily accomplish this?
Thanks
To summarize really quick: What are higher-order components?
Just a fancy name for a simple concept: Simply put: A component that takes in a component and returns you back a more enhanced version of
the component.
We are essentially enhancing a component.
Accepts a function that maps owner props to a new collection of props
that are passed to the base component.
We are basically passing the props down from that BaseComponent down
to the Wrapped Component so that we can have them available in that
child component below:
Use to compose multiple higher-order components into a single
higher-order component.
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { AddComment } from '../comments/add.jsx';
const mapProps = propFunction => Component => (props) => {
return React.createFactory(Component)(propFunction(props));
};
const compose = (propFunction, ComponentContainer) => (BaseComponent) => {
return propFunction(ComponentContainer(BaseComponent));
};
const Review = AddComment(({ handleReviewToggle }) => (
<div className="container">
<ReviewButton
primaryText="Add Comment"
_onClick={handleReviewToggle}
/>
</div>
));
export default Review;
// ================================================================== //
const EnhanceReview = compose(withProps, AddComment)(Review);
const withProps = mapProps(({ ...props }) => ({ ...props }));
The AddComment Container that will have the button and the dialog itself.
export function AddComment(ComposedComponent) {
class AC extends React.Component {
constructor() {
super();
this.state = {open: false};
}
handleReviewToggle = () => {
this.setState({ open: !this.state.open })
}
render() {
return (
<ComposedComponent
{...this.props}
{...this.state}
{...{
handleReviewToggle: this.handleReviewToggle,
}}
/>
);
}
}
export default AddComment;
// ==================================================================
The ReviewButton Button that will fire an event to change state true or false.
const ReviewButton = ({ _onClick, primaryText }) => {
return (
<Button
onClick={_onClick}
>
{primaryText || 'Default Text'}
</Button>
);
};
export default ReviewButton;
// ================================================================== //
However this was all done without using a library. There's one out called recompose here: https://github.com/acdlite/recompose. I highly suggest that you try it out without a library to get a good understanding of Higher Order Components.
You should be able to answer these questions below after playing with Higher Order components:
What is a Higher Order Component?
What are the disadvantages of using HOC? What are some use cases?
How will this improve performance? And how can I use this to optimize for performance?
When is the right time to use a HOC?
I have a seemingly trivial question about props and function components. Basically, I have a container component which renders a Modal component upon state change which is triggered by user click on a button. The modal is a stateless function component that houses some input fields which need to connect to functions living inside the container component.
My question: How can I use the functions living inside the parent component to change state while the user is interacting with form fields inside the stateless Modal component? Am I passing down props incorrectly?
Container
export default class LookupForm extends Component {
constructor(props) {
super(props);
this.state = {
showModal: false
};
}
render() {
let close = () => this.setState({ showModal: false });
return (
... // other JSX syntax
<CreateProfile fields={this.props} show={this.state.showModal} onHide={close} />
);
}
firstNameChange(e) {
Actions.firstNameChange(e.target.value);
}
};
Function (Modal) Component
const CreateProfile = ({ fields }) => {
console.log(fields);
return (
... // other JSX syntax
<Modal.Body>
<Panel>
<div className="entry-form">
<FormGroup>
<ControlLabel>First Name</ControlLabel>
<FormControl type="text"
onChange={fields.firstNameChange} placeholder="Jane"
/>
</FormGroup>
);
};
Example: say I want to call this.firstNameChange from within the Modal component. I guess the "destructuring" syntax of passing props to a function component has got me a bit confused. i.e:
const SomeComponent = ({ someProps }) = > { // ... };
You would need to pass down each prop individually for each function that you needed to call
<CreateProfile
onFirstNameChange={this.firstNameChange}
onHide={close}
show={this.state.showModal}
/>
and then in the CreateProfile component you can either do
const CreateProfile = ({onFirstNameChange, onHide, show }) => {...}
with destructuring it will assign the matching property names/values to the passed in variables. The names just have to match with the properties
or just do
const CreateProfile = (props) => {...}
and in each place call props.onHide or whatever prop you are trying to access.
I'm using react function component
In parent component first pass the props like below shown
import React, { useState } from 'react';
import './App.css';
import Todo from './components/Todo'
function App() {
const [todos, setTodos] = useState([
{
id: 1,
title: 'This is first list'
},
{
id: 2,
title: 'This is second list'
},
{
id: 3,
title: 'This is third list'
},
]);
return (
<div className="App">
<h1></h1>
<Todo todos={todos}/> //This is how i'm passing props in parent component
</div>
);
}
export default App;
Then use the props in child component like below shown
function Todo(props) {
return (
<div>
{props.todos.map(todo => { // using props in child component and looping
return (
<h1>{todo.title}</h1>
)
})}
</div>
);
}
An addition to the above answer.
If React complains about any of your passed props being undefined, then you will need to destructure those props with default values (common if passing functions, arrays or object literals) e.g.
const CreateProfile = ({
// defined as a default function
onFirstNameChange = f => f,
onHide,
// set default as `false` since it's the passed value
show = false
}) => {...}
just do this on source component
<MyDocument selectedQuestionData = {this.state.selectedQuestionAnswer} />
then do this on destination component
const MyDocument = (props) => (
console.log(props.selectedQuestionData)
);
A variation of finalfreq's answer
You can pass some props individually and all parent props if you really want (not recommended, but sometimes convenient)
<CreateProfile
{...this.props}
show={this.state.showModal}
/>
and then in the CreateProfile component you can just do
const CreateProfile = (props) => {
and destruct props individually
const {onFirstNameChange, onHide, show }=props;