How can i pass props through methods inside components? - javascript

i have a react component named "List" that renders smaller components "Post" using a button through method "Addpost()" that takes 2 props from the input form. I have saved the input in 2 varables but i don't know how to pass these props to the Addpost() method inside the return of List's render().
//=========== List component ==============
class List extends React.Component{
renderPost(title,content){
return(
<Post titolo={title} contenuto={content}/>
);
}
renderPost just render the Post component in a in the HTML
addPost(title,content){
title = document.getElementById("inputTitle").value;
content = document.getElementById("inputContent").value;
console.log(title, content)
this.renderPost(title,content);
}
addPost should take the input value and use renderPost to render the Post component with that title and content
render(){
return(
<div>
{this.renderPost("testTitle","testContent")}
<form>
Title:<br></br>
<input type="text" id="inputTitle"/><br></br>
Content:<br></br>
<input type="text" id="inputContent"/>
</form><br></br>
<button className="square"
how can i make this work? title and content are not defined
onClick={() =>
this.addPost(title,content)
Add Post!
</button>
</div>
);
}
}
//=========== Post component ==============
class Post extends React.Component {
render() {
return (
<li className="w3-padding-16">
<img src="/w3images/workshop.jpg" alt="Imagedf" className="w3-left w3-margin-right" />`enter code here`
<span className="w3-large">
{this.props.titolo}
</span><br></br>
<span>{this.props.contenuto}</span>
</li>
);
}
}

Basically, whenever you're dealing with forms and inputs, you would use refs.
App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import PostList from './components/PostList'
import AddPostForm from './components/AddPostForm'
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
posts: [] //state is handled here
}
this.addPost = this.addPost.bind(this)
}
addPost(title, content) {
let newPost = { title, content }
this.setState(({ posts }) => { return { posts: [...posts, newPost] } } )
}
render() {
const { posts } = this.state
return (
<div>
<AddPostForm onNewPost={this.addPost} /> //we pass addPost to the component
<br />
<PostList posts={posts} />
</div>
);
}
}
export default App;
Post.js
import React from 'react';
function Post({titolo, contenuto}) {
return (
<li className="w3-padding-16">
<img src="/w3images/workshop.jpg" alt="Imagedf" className="w3-left w3-margin-right" />`enter code here`
<span className="w3-large">
{titolo}
</span><br></br>
<span>{contenuto}</span>
</li>
);
}
export default Post
AddPostForm.js
import React from 'react';
const addPostForm = ({onNewPost = f => f}) => { //onNewPost method is passed by props from the parent
let _titleInput, _contentInput //these are our refs, see the docs for more information
const submit = (e) => {
e.preventDefault()
onNewPost(_titleInput.value, _contentInput.value) //here we call the addPost function that was passed to the component
_titleInput.value = '' //empty the inputs
_contentInput.value = ''
_titleInput.focus() //set focus
}
return (
<form onSubmit={submit}>
Title:<br></br>
<input type="text" ref={title => _titleInput = title} /><br></br>{/* Note the ref attribute */}
Content:<br></br>
<input type="text" ref={content => _contentInput = content} />
<button className="square">Add a new post</button>
</form>
)
}
export default addPostForm
PostList.js
import React from 'react';
import Post from './Post';
const PostList = ({ posts=[] }) => {
return (
<div className="post-list">
{
posts.map((post, index) =>
<Post key={index} titolo={post.title} contenuto={post.content} />
)
}
</div>
)
}
export default PostList
And the result:

edit
renderPost just render the Post component in a in the HTML
state = { inputTitle: '', inputContent: '' }
addPost(title,content){
title = document.getElementById("inputTitle").value;
content = document.getElementById("inputContent").value;
console.log(title, content)
this.renderPost(title,content);
}
addPost should take the input value and use renderPost to render the Post
component with that title and content
render(){
return(
<div>
{this.renderPost("testTitle","testContent")}
<form>
Title:<br></br>
<input type="text" value={this.inputTitle} onChnage={event => setState({ inputTitle: event.target.value }) }><br></br>
Content:<br></br>
<input type="text" value={this.inputContent} onChnage={event => setState({ inputContent: event.target.value }) } />
</form><br></br>
<button className="square"
on click function
onClick={() =>
this.addPost(this.inputTitle,this.inputContent)
Add Post!
</button>
</div>
);
}
}

Related

React: Render and link toggle button outside the class

I have the following example where the toggleComponent.js is working perfectly.
The problem here is that I don't want to render the <ContentComponent/> inside the toggle, rather I want the opposite, I want to toggle the <ContentComponent/> that will be called in another component depending on the state of the toggle.
So the <ContentComponent/> is outside the toggleComponent.js, but they are linked together. So I can display it externally using the toggle.
An image to give you an idea:
Link to funtional code:
https://stackblitz.com/edit/react-fwn3rn?file=src/App.js
import React, { Component } from "react";
import ToggleComponent from "./toggleComponent";
import ContentComponent from "./content";
export default class App extends React.Component {
render() {
return (
<div>
<ToggleComponent
render={({ isShowBody, checkbox }) => (
<div>
{isShowBody && <h1>test</h1>}
<button onClick={checkbox}>Show</button>
</div>
)}
/>
<ToggleComponent
render={({ isShowBody, checkbox }) => (
<div>
{isShowBody && (
<h1>
<ContentComponent />
</h1>
)}
<button onClick={checkbox}>Show</button>
</div>
)}
/>
</div>
);
}
}
Bit tweaked your source.
Modified ToggleComponent
import React from "react";
export default class ToggleComponent extends React.Component {
constructor() {
super();
this.state = {
checked: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick = () => {
this.setState({ checked: !this.state.checked });
this.props.toggled(!this.state.checked);
};
checkbox = () => {
return (
<div>
<label>Toggle</label>
<span className="switch switch-sm">
<label>
<input type="checkbox" name="select" onClick={this.handleClick} />
<span />
</label>
</span>
</div>
);
};
render() {
return this.checkbox();
}
}
Added OtherComponent with ContentComponent inside.
import React, { Component } from "react";
import ContentComponent from "./content";
export default class OtherComponent extends React.Component {
render() {
return <div>{this.props.show ? <ContentComponent /> : null}</div>;
}
}
Separated as per your requirement.
Modified App
import React, { Component, PropTypes } from "react";
import ToggleComponent from "./toggleComponent";
import OtherComponent from "./otherComponent";
export default class App extends React.Component {
constructor() {
super();
this.toggled = this.toggled.bind(this);
this.state = { show: false };
}
toggled(value) {
this.setState({ show: value });
}
render() {
return (
<div>
<ToggleComponent toggled={this.toggled} />
<OtherComponent show={this.state.show} />
</div>
);
}
}
Working demo at StackBlitz.
If you want to share states across components a good way to do that is to use callbacks and states. I will use below some functional components but the same principle can be applied with class based components and their setState function.
You can see this example running here, I've tried to reproduce a bit what you showed in your question.
import React, { useState, useEffect, useCallback } from "react";
import "./style.css";
const ToggleComponent = props => {
const { label: labelText, checked, onClick } = props;
return (
<label>
<input type="checkbox" checked={checked} onClick={onClick} />
{labelText}
</label>
);
};
const ContentComponent = props => {
const { label, children, render: renderFromProps, onChange } = props;
const [checked, setChecked] = useState(false);
const defaultRender = () => null;
const render = renderFromProps || children || defaultRender;
return (
<div>
<ToggleComponent
label={label}
checked={checked}
onClick={() => {
setChecked(previousChecked => !previousChecked);
}}
/>
{render(checked)}
</div>
);
};
const Holder = () => {
return (
<div>
<ContentComponent label="First">
{checked => (
<h1>First content ({checked ? "checked" : "unchecked"})</h1>
)}
</ContentComponent>
<ContentComponent
label="Second"
render={checked => (checked ? <h1>Second content</h1> : null)}
/>
</div>
);
};
PS: A good rule of thumb concerning state management is to try to avoid bi-directional state handling. For instance here in my example I don't use an internal state in ToggleComponent because it would require to update it if given checked property has changed. If you want to have this kind of shared state changes then you need to use useEffect on functional component.
const ContentComponent = props => {
const { checked: checkedFromProps, label, children, render: renderFromProps, onChange } = props;
const [checked, setChecked] = useState(checkedFromProps || false);
const defaultRender = () => null;
const render = renderFromProps || children || defaultRender;
// onChange callback
useEffect(() => {
if (onChange) {
onChange(checked);
}
}, [ checked, onChange ]);
// update from props
useEffect(() => {
setChecked(checkedFromProps);
}, [ checkedFromProps, setChecked ]);
return (
<div>
<ToggleComponent
label={label}
checked={checked}
onClick={() => {
setChecked(previousChecked => !previousChecked);
}}
/>
{render(checked)}
</div>
);
};
const Other = () => {
const [ checked, setChecked ] = useState(true);
return (
<div>
{ checked ? "Checked" : "Unchecked" }
<ContentComponent checked={checked} onChange={setChecked} />
</div>
);
};

change object into array present another component in react

i am sorry for the very basic question as i m new to react js. i have created an array in a react component and render it through map function in one component and i want to change (add/Subject) in the array from an other component on basis of _id. the following is a sample that helps you better to understand what i actually want. Thanks in advance Sir
{*Array Component*}
const ArrayData =[
{
_id:1,
title:"All Searches"
},
{
_id:5,
title:1
},
{
_id:6,
title:"4"
}
]
export default ArrayData;
{*2nd Component*}
import react from "react"
import ArrayData from ArrayComponent
class Parent extends React.Component {
constructor() {
super();
this.state = {
ArrayData:ArrayData,
collapsed: false,
}
}
render() {
const { ArrayData } = this.state;
return (
<>
<FirstChild Data={ArrayData} />
<SecondChild />
</>
);
}
}
export default Parent;
!------------------------------------------!
{*FirstChild*}
class FirstChild extends React.Component {
constructor(props){
super();
this.state={
ArrayData:props.ArrayData
}
}
render() {
const { ArrayData} = this.state;
const renderArray = ArrayData.slice(0, 5).map(Object => {
return <h1>{object._id} </h1>
})
return (
<>
{renderArray}
</>
);
}
}
export default FirstChild;
!-----------------------------------------!
{*SecondChild*}
import { React } from "react";
const SecondChild = () => {
const handleUpdate=(_id, Title) =>{
{*function that can add the inputs as a object into that arrayComponent*}
}
const handleDelete=(_id) =>{
{*function that can delete a object from that arrayComponent having the id given by User in the feild*}
}
return (
<>
<input type='text' name='_id' placeHolder="Which object you want to delete" />
<button type=Submit onClick={handleDelete} >Delete</button>
<br></br>
<input type='text' name='_id' />
<input type='text' name='title' />
<button type=Submit onClick={handleUpdate} >Update</button>
</>
);
}
export default SecondChild;
CODESANDBOX
All you need to do is declare handleDelete and handleUpdate on the Parent component and pass it as a props in SecondChild component. If we place state and its methods in Parent component, then it is easy to track, debug, and easy to maintain. It would be easy to pass the methods if we define another component, let say ThirdComponent and it also contains functionality to perform CRUD operation on the ArrayData array.
In the FirstChild component, you were destructuring the ArrayData const { ArrayData} = this.state; and using it in the render method. It won't update we receive new props because you are rendering the array state that gets created once(as a constructor will get called once) and we want the latest value of ArrayData from the parent component. We can use props directly in the render method. You need to see the react lifecycle methods.
Parent.js
import React from "react";
import ArrayData from "./ArrayComponent";
import FirstChild from "./FirstChild";
import SecondChild from "./SecondChild";
class Parent extends React.Component {
constructor() {
super();
this.state = {
ArrayData: ArrayData,
collapsed: false
};
this.handleDelete = this.handleDelete.bind(this);
this.handleUpdate = this.handleUpdate.bind(this);
}
handleDelete(id) {
const idToDelete = parseInt(id, 10);
this.setState((state) => {
const filteredArrayData = state.ArrayData.filter(
(el) => el._id !== idToDelete
);
return {
ArrayData: filteredArrayData
};
});
}
handleUpdate(newObj) {
console.log(newObj);
this.setState((state) => ({
ArrayData: [...state.ArrayData, newObj]
}));
}
render() {
return (
<>
<FirstChild Data={this.state.ArrayData} />
<SecondChild
handleUpdate={this.handleUpdate}
handleDelete={this.handleDelete}
/>
</>
);
}
}
export default Parent;
FirstChild.js
import React from "react";
class FirstChild extends React.Component {
render() {
return (
<>
{this.props.Data.slice(0, 5).map((el) => {
return <h1 key={el._id}>{el._id}</h1>;
})}
</>
);
}
}
export default FirstChild;
SecondChild.js
import React, { useState } from "react";
const SecondChild = ({ handleUpdate, handleDelete }) => {
const [idToDelete, setIdToDelete] = useState(null);
const [newID, setNewID] = useState(null);
const [newTitle, setNewTitle] = useState("");
return (
<>
<input
name="_id"
type="number"
onChange={(e) => setIdToDelete(e.target.value)}
placeholder="Which object you want to delete"
/>
<button type="submit" onClick={() => handleDelete(idToDelete)}>
Delete
</button>
<br></br>
<br></br>
<br></br>
<input
type="text"
name="_id"
placeholder="id"
onChange={(e) => setNewID(e.target.value)}
/>
<input
type="text"
name="title"
placeholder="title"
onChange={(e) => setNewTitle(e.target.value)}
/>
<button
type="submit"
onClick={() => handleUpdate({ _id: newID, title: newTitle })}
>
Update
</button>
</>
);
};
export default SecondChild;

Unable to setState on props in react functional component

I have been unable to setState on props I keep getting
TypeError: props.setState is not a function
I'm trying to implement a search function
const HeroComp = (props) => {
let handleSearchSubmit = (e) => {
props.setState({searchValue: e.target.value});
}
return <div className='heroComp' >
<form action="" >
<input type="text" placeholder='search cartigory' onChange={handleSearchSubmit} />
</form>
</div>
}
export default HeroComp;
When I console.log(props) I get
{searchValue: ""}
searchValue: ""
__proto__: Object
This is the parent component
import images from '../data/images'; //the file from which i'm importing images data
class HomePage extends React.Component{
constructor(){
super();
this.state = {
images,
searchValue: ''
}
}
render(){
const {images , searchValue} = this.state;
const filteredImage = images.filter(image => image.cartigory.toLowerCase().includes(searchValue));
return(
<div >
<HeroComp searchValue={ searchValue } />
<GalleryComp filteredImage={filteredImage} />
</div>
)
}
}
export default HomePage;
I know this should be easy but I just can't see the solution .
How about this?
useEffect(() => {
// set the current state
setSearchValue(props.searchValue)
}, [props]);
Functional component dont have state, but you can use reactHooks:
import React, { useState } from 'react';
const HeroComp = (props) => {
let [searchValue, setSearchValue] = useState();
let handleSearchSubmit = (e) => {
setSearchValue(e.target.value);
}
return <div className='heroComp' >
<form action="" >
<input type="text" placeholder='search cartigory' onChange={handleSearchSubmit} />
</form>
</div>
}
export default HeroComp;

Passing more than one props to one component

How can I pass more than one props to one component?
For example, I want to render a DIV that has various elements but not all the elements are in the same component.
So here I'm passing the whole component into another component, the problem with this is that ContentSkuInfo component is mapping throw states so all the data is loading in the same place, I need to load de data but ones in each DIV.
I need this:
<div>
<strong>Data 01</strong>
</div>
<div>
<strong>Data 02</strong>
</div>
<div>
<strong>Data 03</strong>
</div>
But I'm having this:
<div>
<strong>Data 01</strong>
<strong>Data 02</strong>
<strong>Data 03</strong>
</div>
This are my components Seccion_uno_contenido.js
import React from 'react';
import ContentSkuInfo from './CallDataStorage';
const ContenidoUno = (props) => {
return (
<div className="contentBox outerBox-first" >
<a href={props.link}>
<img src={props.imagen} alt="bloque 01" className="img-wrap" />
</a>
<h3>{props.categoria}</h3>
<img src={props.icono} className="iconic" alt="producto" />
<span>{props.descripcion}</span>
<ContentSkuInfo />
<small>Antes: ${props.antes}</small>
<div className="containerBotonRow">
¡Lo quiero!</button>
</div>
</div>
);
}
export default ContenidoUno;
CallDataStorage.js
import React from 'react';
import axios from 'axios';
var productsIds = ['3552357', '2635968BC', '3181464', '3593754'];
var productsIdsJoin = productsIds.join('-');
const getProductDetailAPI = (productsIds, storeId) => ({
method: 'GET',
baseURL:`SORRY CAN'T SHOW THIS`,
auth: {
username: 'XXXXXXX',
password: 'XXXXXXX',
},
headers: {
'Content-Type': 'application/json',
},
data: {},
});
const ContentSkuInfo = (props) => {
return (
<strong>$ {props.prodsNormal}</strong>
);
}
class DataStorage extends React.Component {
constructor(props) {
super(props);
this.state = { products: [] };
};
getWebServiceResponse = (currentList, storeId) => (
axios(getProductDetailAPI(currentList, storeId))
.then(response => response.data)
.then(newData => {
this.setState({ products: newData.productDetailsJson })
})
.catch(e => e)
);
componentDidMount() {
this.getWebServiceResponse(productsIdsJoin, 96);
};
render() {
return (
<samp>
{this.state.products.map(skuData =>
<ContentSkuInfo
prodsName={skuData.name}
prodsId={skuData.productId}
prodsStatus={skuData.status}
prodsPublished={skuData.published}
prodsNormal={skuData.NORMAL}
prodsCMR={skuData.CMR}
prodsAhorro={skuData.savings}
prodsCombo={skuData.combo}
prodsStock={skuData.stockLevel}
/>
)
}
</samp>
)
}
}
export default DataStorage;
Just change the definition of ContentSkuInfo in CallDataStorage.js to
const ContentSkuInfo = (props) => (
<div>
<strong>$ {props.prodsNormal}</strong>
</div>
)
You are not passing any props to ContentSkuInfo component in your code. To access something via props inContentSkuInfo you need to pass some properties to ContentSkuInfo.
For eg
When you are calling ContentSkuInfo In your ContenidoUno component you need to send something like below
<ContentSkuInfo prodsNormal=“testing” prodsNormal2=“test2”/>
And in ContentSkuInfo component you can get it like remove $ from expression
Without return
const ContentSkuInfo = (props) => (
<div>
<div>
<strong>{props.prodsNormal}</strong>
</div>
<div>
<strong>{props.prodsNormal2}</strong>
</div>
</div>
)
With return
const ContentSkuInfo = (props) => {
return (
<div>
<div>
<strong>{props.prodsNormal}</strong>
</div>
<div>
<strong>{props.prodsNormal2}</strong>
</div>
</div>
)}

How do I add React component on button click?

I would like to have an Add input button that, when clicked, will add a new Input component. The following is the React.js code that I thought is one way of implementing the logic that I want, but unfortunately it's doesn't work.
The exception that I got is:
invariant.js:39 Uncaught Invariant Violation: Objects are not valid as
a React child (found: object with keys {input}). If you meant to
render a collection of children, use an array instead or wrap the
object using createFragment(object) from the React add-ons. Check the
render method of FieldMappingAddForm.
How do I solve this problem?
import React from 'react';
import ReactDOM from "react-dom";
class Input extends React.Component {
render() {
return (
<input placeholder="Your input here" />
);
}
}
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {inputList: []};
this.onAddBtnClick = this.onAddBtnClick.bind(this);
}
onAddBtnClick(event) {
const inputList = this.state.inputList;
this.setState({
inputList: inputList.concat(<Input key={inputList.length} />)
});
}
render() {
return (
<div>
<button onClick={this.onAddBtnClick}>Add input</button>
{this.state.inputList.map(function(input, index) {
return {input}
})}
</div>
);
}
}
ReactDOM.render(
<Form />,
document.getElementById("form")
);
React Hook Version
Click here for live example
import React, { useState } from "react";
import ReactDOM from "react-dom";
const Input = () => {
return <input placeholder="Your input here" />;
};
const Form = () => {
const [inputList, setInputList] = useState([]);
const onAddBtnClick = event => {
setInputList(inputList.concat(<Input key={inputList.length} />));
};
return (
<div>
<button onClick={onAddBtnClick}>Add input</button>
{inputList}
</div>
);
};
ReactDOM.render(<Form />, document.getElementById("form"));
Remove {}., it is not necessary using it in this case
{this.state.inputList.map(function(input, index) {
return input;
})}
Example
or better in this case avoid .map and just use {this.state.inputList},
Example
const [visible,setVisible] = useState(false);
return(
<>
<button onClick={()=>setVisible(true)}>Hit me to add new div</button>
{
visible ? (
<div id='addThisContainer'>
{/* Your code inside */}
</div>
) : null
}
</>
)

Categories

Resources