I'm trying to get the value of child component yet not successful. Here what I am working on ...
import React from "react";
import Tooltip from "rc-tooltip";
import Slider, { Range } from "rc-slider";
const Handle = Slider.Handle;
const handle = props => {
const { value, dragging, index, ...restProps } = props;
return (
<Tooltip
prefixCls="rc-slider-tooltip"
overlay={value}
visible={dragging}
placement="top"
key={index}
>
<Handle value={value} {...restProps} />
</Tooltip>
);
};
const Slider = props => {
return (
<div>
<div style={{ width: 300, margin: 30 }}>
<p>{this.props.title}</p>
<Slider min={0} max={10} defaultValue={5} handle={handle}/>
</div>
</div>
);
};
export default Slider;
Main App.js
import Slider from '.....'
class App extends Component{
constructor(props){
super(props);
this.state = {
val: 0
}
}
render() {
return(
<Slider onChange={this.state.value} />
)
}
}
I am looking to get the value to be updated to this App.js state as the slider is being dragged. onChange is not updating the state. How should I modify so that slider value gets updated on this.state.value.
The Slider component from rc-slider has an onChange prop event which is a function. You need to pass this method to slider and update the state instead of just passing the state value
import React from "react";
import Tooltip from "rc-tooltip";
import Slider, { Range } from "rc-slider";
const Handle = Slider.Handle;
const handle = props => {
const { value, dragging, index, ...restProps } = props;
return (
<Tooltip
prefixCls="rc-slider-tooltip"
overlay={value}
visible={dragging}
placement="top"
key={index}
>
<Handle value={value} {...restProps} />
</Tooltip>
);
};
const Slider = props => {
return (
<div>
<div style={{ width: 300, margin: 30 }}>
<p>{this.props.title}</p>
<Slider min={0} max={10} defaultValue={5} onChange={props.onChange} handle={handle}/>
</div>
</div>
);
};
export default Slider;
class App extends Component{
constructor(props){
super(props);
this.state = {
val: 0
}
}
onChange=(value) => {
this.setState({val: value});
}
render() {
return(
<Slider onChange={this.onChange} />
)
}
}
Here is the Live code
https://codesandbox.io/s/n9y97y55kp
Let me know if you have any doubts
You just have to pass the event from child to parent component to update the values.
according to rc-slider document, you should pass a function to Slider onChange.
import Slider from '.....'
class App extends Component {
constructor(props) {
super(props);
this.state = {
val: 0
}
}
handleSliderChange = (value) => {
console.log(value) // maybe a string or a object
this.setState({val: value})
}
render() {
return (
<Slider onChange={this.handleSliderChange} />
)
}
}
above code should work
Related
I just want to clear the state when you move to any other screen in my class component when I'm using navigation v6 (I saw some coments about function component but not class), so I will explain briefly
I did a clean state function in my Body component
class InputBody extends Component {
constructor(props) {
super(props);
this.state = {
fields: JSON.parse(this.props.route).message,
};
}
reset () {
this.setState({});
}
render() {
return (
<Fragment>
{Object.keys(JSON.parse(this.props.route).message).length > 0 ? (
<FieldArraysForm all={JSON.parse(this.props.route).message} resetForm={reset} native={this.props} />
) : (
<ActivityIndicator size="large" color="#eb6b09" />
)}
</Fragment>
);
}
}
And then I call it in my fields array component
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
class FieldArraysForm extends Component {
render() {
const {handleSubmit} = this.props.native;
// event listener
const getFields = async (values) => {
return sleep(500).then(() => {
console.log(JSON.stringify(values))
this.props.resetForm();
})
}
return (
<Form>
{this.props.all.map((item) => (
<Field
key={item._id}
name={`customInput.${item._id}`}
component={RenderField}
label={item.field}
type={item.typeFieldAltText}
/>
))}
<View>
<TouchableOpacity onPress={handleSubmit(getFields)}>
<Text>Save Form</Text>
</TouchableOpacity>
</View>
</Form>
);
}
}
Render field function component
class RenderField extends Component {
render() {
return (
<Fragment>
<Texto>{this.props.label}</Texto>
<TextInput
onChangeText={this.props.input.onChange}
{...this.props.input}
keyboardType={this.props.type}
/>
</Fragment>
);
}
}
So how can I call this.prop.reset() on screen change using class component with react navigation v6 ?
I'm currently creating a search bar where App is the parent and Header, Home are the children. I'm trying to pass the input from Header to Home via App but it seems that when I try to load in {this.state.searchValue} it does nothing.
I'm lost of where I am wrong in my code. I'm also using Route to route the props from Header to Home.
Here is my code:
Header.js (Child 1)
class Header extends Component {
constructor(props) {
super(props);
this.state = {
search: "",
};
}
onChange = (event) => {
this.setState({ search: event.target.value });
};
submitSearch = (event) => {
event.preventDefault();
console.log(this.state.search);
this.props.passSearchData(this.state.search);
};
render() {
return (
<React.Fragment>
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<form className="form-inline">
<input
className="form-control mr-sm-2"
type="text"
placeholder="Search"
onChange={this.onChange}
/>
<button
className="btn btn-danger"
type="submit"
onClick={this.submitSearch}
>
Search
</button>
</form>
</nav>
</React.Fragment>
);
}
}
export default Header;
App.js (Parent)
class App extends Component {
constructor() {
super();
this.state = {
searchValue: "",
};
}
handleSearchData = (search) => {
this.setState({ searchValue: search });
};
componentDidMount() {
this.props.getItems();
}
render() {
return (
<div className="App">
<Router>
<Header passSearchData={this.handleSearchData} />
<Route
exact
path="/"
render={(props) => (
<Home {...props} searchValue={this.state.searchValue} />
)}
/>
</Router>
<Footer />
</div>
);
}
}
Home.js
class Catalog extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<div>
<p>{this.props.searchValue}</p>
</div>
);
}
}
I think using react context better for this problem, because passing state between routes quite painful
First u declare your own Provider to act as intermediary between components.
The context will save all the application state. and to consume at your components, simply use useContext and pass the Context u want to use, at this useCase, i call it AppContext. by using the same context, your components get the same state and trigger update immediately
The solution i provide is using functional component. If u are using class Component, just simply create a functional component, then pass the context to the class component
import React, { useContext, useState } from 'react'
const AppContext = React.createContext({})
const AppProvider = props => {
const [currentState, setCurrentState] = useState(null)
const handleState = value => {
setCurrentState(value)
}
const contextValue = { handleState, currentState }
return (
<AppContext.Provider value={contextValue}>
{props.children}
</AppContext.Provider>
)
}
const Child1 = props => {
const { handleState } = useContext(AppContext)
const handleClick = e => {
handleState(e.target.values)
}
return (
<button onClick={handleClick}>Change State</button>
)
}
const Child2 = props => {
const { currentState } = useContext(AppContext)
return (
<h1>{currentState}</h1>
)
}
const Parent = props => {
return (
<Router>
<AppProvider>
<Route component={Child1} />
<Route component={Child2} />
</AppProvider>
</Router>
)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
import React, { useReducer, useEffect ,Component} from "react";
import ReactDOM from "react-dom";
const AppContext = React.createContext();
import React from "react";
const withRandomColors = WrappedComponent => {
return class RandomColors extends React.Component {
constructor() {
super();
this.randomColors = [
"red",
"blue",
"green",
"cyan",
"lavender",
"skyblue",
"orange",
"pink",
"yellow"
];
}
getRandomColors() {
const num = Math.floor(Math.random() * 10) % 9;
return this.randomColors[num];
}
render() {
console.log("Rerendering wrapper Component");
return <WrappedComponent randomColor={this.getRandomColors()} />;
}
};
};
class Number extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {}
render() {
console.log("rendering Number Component");
return (
<AppContext.Consumer>
{({ number }) => {
return (
<div style={{ backgroundColor: `${this.props.randomColor}` }}>
{number} <br />
</div>
);
}}
</AppContext.Consumer>
);
}
}
class Text extends React.Component {
constructor(props) {
super(props);
}
render() {
console.log("rendering Text Component");
return (
<AppContext.Consumer>
{({ text }) => (
<div style={{ backgroundColor: `${this.props.randomColor}` }}>
{text} <br />
</div>
)}
</AppContext.Consumer>
);
}
}
const WrappedText=withRandomColors(Text);
const WrappedNumber=withRandomColors(Number);
class App extends Component {
constructor() {
super();
this.state = {
number: Math.random() * 100,
text: "testing context api"
};
}
updateNumber = () => {
const randomNumber = Math.random() * 100;
this.setState({ number: randomNumber });
};
render() {
console.log("rendering app")
return (
<AppContext.Provider value={this.state}>
<div>
<h1>Welcome to React</h1>
<WrappedNumber />
<WrappedText />
<button onClick={this.updateNumber}>Change Number </button>
</div>
</AppContext.Provider>
);
}
}
ReactDOM.render(
<App />,
mountNode
);
when clicked on ChangeNumber button console displays
rendering app
Rerendering wrapper Component
Rerendering Number Component
Rerendering wrapper Component
Rerendering Text Component
and background color changes for both number and text.
React context is supposed to rerender only consumers for the provider right? why is it rerendering All the children of Provider?
I expected that only number changes keeping background color of Number and Text as same and below output from console once clicked on Change Number button since only consumers are supposed to rerender not the Text and Number components.
rendering app
what am I missing?
I took the code from sandbox
WrappedNumber and WrappedText re-render when you update a state in the App component because in the Virtual DOM they come in the same hierarchy as the Provider and when the parent component updates the child components are updated too.
To avoid them from re-rendering you could provide them as children of App like
class App extends Component {
constructor() {
super();
this.state = {
number: Math.random() * 100,
text: "testing context api"
};
}
updateNumber = () => {
const randomNumber = Math.random() * 100;
this.setState({ number: randomNumber });
};
render() {
console.log("rendering app")
return (
<AppContext.Provider value={this.state}>
<div>
<h1>Welcome to React</h1>
{this.props.children}
<button onClick={this.updateNumber}>Change Number </button>
</div>
</AppContext.Provider>
);
}
}
ReactDOM.render(
<App >
<WrappedNumber />
<WrappedText />
</App>,
mountNode
);
I need to figure out how to transfer the data i receive in a child component to the one in parent component. I need to set the console log i receive in the child to transfer to the parent component state.
Currently I have:
Child Comp:
import Picker from 'react-giphy-component'
import React, { Component, PropTypes } from 'react'
class AddGif extends Component {
log (gif) {
console.log(gif.original.mp4)
}
returnedGifObject = gif ;
render () {
return (
<div>
{/* <button></button> */}
<Picker onSelected={this.log.bind(this)} />
</div>
)
}
}
export default AddGif;
parent element
class PostBox extends Component {
constructor(props){
super(props)
this.state = {
title: null,
postBody: null,
giphyUrl: null,
displayGifPicker: false
}
}
getGifState = (selectedUrl) => {
this.setState({ giphyUrl: selectedUrl})
}
render () {
const {title, postBody} = this.state
const displayGifPicker = this.state.displayGifPicker
return (
<Grid item xl={8}>
{/* <Card className={classes.card} style={mt4}> */}
<Card style={mt4}>
<CardContent >
<PostInput onChange={this.handleInputChange} onSubmit={this.handleSubmit}/>
{displayGifPicker ? (<AddGif selectedGif = {this.getGifState} />) : (<Button size="small" onClick={this.displayGifPicker} ><button>Add Gif</button></Button>)}
<CardActions>
{/* <Button size="small">Submit VH5</Button> */}
</CardActions>
</CardContent>
</Card>
</Grid>
)
}
}
You passed the function prop to children component. Then In Children component just call it :
log = (gif) => {
const { selectedGif } = this.props
console.log(gif.original.mp4)
selectedGif(gif.original.mp4)
}
When I type something in my textarea, and then click on the button, this new element is stocked inside an array and displayed in a list in my react app. I want the array's elements to be crossed when I click on them.
I've written a function to change the state of 'crossed' to its opposite when i click on the element, and then the style of the elements would change depending on whether it's true or false.
app.js:
import React from 'react';
import Tasks from './tasks.js';
import Item from './component.js';
import './App.css';
class App extends React.Component {
state = {
todolist: [],
crossed: false
}
addData(val) {
this.setState({ todolist: this.state.todolist.concat(val) },
() => console.log(this.state.todolist))
}
cross() {
this.setState({ crossed: !this.state.crossed },
() => console.log(this.state.crossed))
}
render() {
return (
<div className="App">
<Tasks onClick={value => this.addData(value)} />
{
(this.state.crossed) ? (<ul>
{this.state.todolist.map((e) => {
return <Item
item={e}
onClick={(e) => this.cross(e)}
style={{ textDecoration : 'line-through' }} />}
)
}
</ul>) : (
<ul>
{this.state.todolist.map((e) => {
return <Item
item={e}
onClick={(e) => this.cross(e)}
/>}
)
}
</ul>
)
}
</div>
);
}
}
export default App;
component.js:
import React from 'react'
class Item extends React.Component{
render(){ return(
<li onClick={this.props.onClick} style={this.props.style}>{this.props.item}
</li>
);
}}
export default Item
tasks.js :
import React from 'react'
class Tasks extends React.Component {
constructor(props) {
super(props)
this.state = {
value: '',
}
}
handleChange = e => {
this.setState({ value: e.target.value })
}
render() {
return (<div>
<textarea value={this.state.value} onChange={this.handleChange} ></textarea>
<button onClick={() => this.props.onClick(this.state.value)}>Add task</button>
</div>)
}
}
export default Tasks
I want each element to be crossed on its own when I click on it, but all the elements get crossed when I click on any one of them.
You should have some key for each object to differentiate,
addData(val) {
const tempObj = {
val: val,
crossed: false
}
this.setState({ todolist: this.state.todolist.concat(tempObj) },
() => console.log(this.state.todolist))
}
Now you will have crossed key for each object. I have not run the code, but this should work.
cross = e => {
e.crossed = !e.crossed;
}
(
<ul>
{this.state.todolist.map(e => {
return <Item
item={e}
onClick={(e) => this.cross(e)}
style={e.crossed && { textDecoration : 'line-through' }} />} // use ternary operator or this kind of && condition here
)
}
</ul>
)