How to access the nested object in react js - javascript

I have handle click and I want to access the className of the div element, how can I do that
this is the element i want to get the div className
<div className="milad">
<IconButton
style={{outline:'none', color: 'white', float:'right', marginRight:'20px', marginTop: "5px"}}
className = "iconButton"
>menu
<MenuIcon/>
</IconButton>
</div>
this is my
checkHandleClick = (event) => {
console.log(event);
}
-----------------------------------------------------------------
<ClickAwayListener onClickAway={((e) => this.checkHandleClick(e))}>
-----some code------
</ClickAwayListener>
I want to access this Console.log

You could do using event.target.className
checkHandleClick = (event) => {
console.log(event.target.className);
}
-----------------------------------------------------------------
<ClickAwayListener onClickAway={((e) => this.checkHandleClick(e))}>
-----some code------
</ClickAwayListener>

You should use ref for it.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
Or if you're using hooks
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}

Related

How do you pass an onClick function to grandchild component in React?

I'm just starting out with React, adapting the tic tac toe tutorial for my case.
I'm trying to click on the grandchild component to change the state of the grandparent component . Code is as follows:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
fields: [
{
id: 1,
show: false
},
{
id: 2,
show: false
}
]
}
}
handleClick(i) {
const fields = this.state.fields.slice();
fields[i].show = true;
this.setState({fields: fields});
}
render() {return <Preview />}
}
const Preview = (props) => {
return (
<div className="preview">
{props.fields.map((field) => (
<Field data={field} key={field.id} onClick={ props.onClick(field.id) }/>
))}
</div>
);
};
const Field = props => {
return (
<div className="field" onClick={ props.onClick } />
);
};
I get a TypeError: Cannot read property 'state' of undefined from this line:
handleClick(i) {
const fields = this.state.fields.slice();
Issues
this of the App class isn't bound to the handleClick function. This is cause of TypeError: Cannot read property 'state' of undefined error.
You are mutating your state object. Slicing the array creates a new array reference, but fields[i].show = true; mutates the object reference in state.
You don't pass fields or onClick props to Preview.
The onClick callback isn't called correctly in Preview.
Solution
Bind this to the handler or convert to arrow function so it is automatically bound.
constructor(props){
...
this.handleClick = this.handleClick.bind(this);
}
or
handleClick = (i) => { ..... };
DON'T MUTATE STATE. Shallow copy state then update properties.
handleClick = (id) => {
this.setState(prevState => ({
fields: prevState.fields.map((field) => {
return field.id === id ? {
...field,
show: true,
} : field;
}),
}));
};
Pass fields and handleClick as onClick to Preview.
render() {
return (
<Preview
fields={this.state.fields}
onClick={this.handleClick}
/>
);
}
Call props.onClick correctly with the id.
{props.fields.map((field) => (
<Field
data={field}
key={field.id}
onClick={() => props.onClick(field.id)}
/>
))}
I've added some explanations, check the comments
// [...]
render() {
// Here you need to pass "fields" and "handleClick" as props:
return <Preview fields={this.state.fields} onClickField={this.handleClick} />
}
}
const Preview = (props) => {
// Here you get the props:
const { fields, onClickField } = props;
// Your onclick was a function call instead of just a function
return (
<div className="preview">
{fields.map((field) => (
<Field
data={field}
key={field.id}
onClick={() => onClickField(field.id) }
/>
))}
</div>
);
};
const Field = props => {
return (
<div className="field" onClick={ props.onClick } />
);
};

update the state of a component from another component

I want to update icon of component from another component. whenever I clicked on a playlist music start playing and icon should be change to pause instead of play but I don't know how I can update the state of a component from another component.
PlayList Component -
playlist and musics are in this component
class PlayList extends React.Component {
render() {
const playMusic = () => {
musics.forEach(e => e.pause());
musics[this.props.arr].play();
musics[this.props.arr].currentTime = 0;
nowPlaying = this.props.arr;
clickedOnMusic = 'clicked';
};
return (
<div>
<Card>
<CardActionArea onClick={playMusic} />
</Card>
</div>
)
}
BottomAppBar Component -
icons and some function to playing music are here
class BottomAppBar extends React.Component {
state = {
displayPlay: 'none',
displayPause: '',
displayVolume: '',
displayMute: 'none'
};
render(){
return(
<IconButton onClick={handleChangePlay} style={{ color: 'white' }}>
<PauseCircleOutlineRoundedIcon
style={{ fontSize: 46, display: this.state.displayPlay }}
/>
<PlayCircleOutlineIcon
style={{ fontSize: 46, display: this.state.displayPause }}
/>
)
}
thank you very much for reading !
Wrap them in a container and maintain their states over there.
Ex:
<Parent>
<PlayList/>
<BottomAppBar />
</Parent>
You can use the context api, any ascendent of PlayerLogic can access whatever you put in the context with React.useContext and will be re rendered when values in the Context change.
const PlayerContext = React.createContext();
const PlayerLogic = ({ children }) => {
const [state, setState] = React.useState({
playing: false,
});
const setPlaying = React.useCallback(
val =>
setState(current => ({ ...current, playing: val })),
[]
);
const pause = React.useCallback(() => setPlaying(false), [
setPlaying,
]);
const play = React.useCallback(() => setPlaying(true), [
setPlaying,
]);
return (
<PlayerContext.Provider
value={{
state,
pause,
play,
}}
>
{children}
</PlayerContext.Provider>
);
};
const ComponentOne = () => {
const {
pause,
play,
state: { playing },
} = React.useContext(PlayerContext);
return (
<div>
{playing ? (
<button onClick={pause}>pause</button>
) : (
<button onClick={play}>play</button>
)}
</div>
);
};
class ComponentTwo extends React.Component {
render() {
return this.context.state.playing
? 'now playing'
: 'nothig is playing';
}
}
ComponentTwo.contextType = PlayerContext;
const A = () => <B />;
const B = () => <C />;
const C = () => {
const {
state: { playing },
} = React.useContext(PlayerContext);
return `In component C, is playing ${JSON.stringify(
playing
)}`;
};
const App = () => (
<PlayerLogic>
<ComponentOne />
<ComponentTwo />
<div>
<A />
</div>
</PlayerLogic>
);
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

React ScrollIntoView not working from parent component

I have a parent component that has a button. When that button is clicked, it should scroll to a grid in the child component, but it is not working. There hasn't been any errors. This is what I have in the parent component:
class ParentComponent extends Component {
constructor(props) {
super(props);
this.state = {
someState: undefined,
};
this.ref_grid = React.createRef();
}
handleClick = () => {
this.setState({
someState: newState,
}, () =>
{
if (this.ref_grid.current !== null) {
this.ref_grid.current.scrollIntoView({
behavior: 'smooth',
block: 'start',
});
}
}
);
}
render() {
return (
<>
<Button
variant="contained"
color="secondary"
size="small"
onClick={() => this.handleClick()}
>Click me!</Button>
<ChildComponent
forwardRef={this.ref_grid}
/>
</>
);
}
}
In the child component I have the ref:
class ChildComponent extends Component {
constructor(props) {
super(props);
this.state = {
open: false
};
}
render() {
const {
classes
} = this.props;
return (
<Grid container spacing={3} ref={this.props.forwardRef}>
</Grid>
)
}
}
I am new to React, so I am not sure if this is the right approach. Would appreciate if anyone has any idea or example how to solve this.
Thank you in advance.

Run function in child component after action in parent component

I'm using refs for calculating block height in child component, it works fine inside, so after each removeHandler() function "doCalculating" is called
But if I tried to call this into parent component, doCalculating() always return the initial value. Like just after componentDidMount()
Seems like doCalculating() into parent component refers to this.refs.tagList.clientHeight just once and not recalc even after child component update
React version 14.7 is used here, so I cannot use hooks
class ChildComponent extends Component {
componentDidMount() {
this.doCalculating()
}
doCalculating = () => {
const defaultHeight = 50
const newHeight = this.refs.tagList.clientHeight
if (newHeight > defaultHeight ) {
// do logic
}
}
render() {
return (
<ul
ref={"tagList"}
>
{array.map((item, index) => (
<li key={index}>
<button>
{item}
<span onClick={
(e) => {
this.removeHandler()
this.doCalculating()
}
} ></span>
</button>
</li>
)
)}
</ul>
)
}
}
class ParentComponent extends Component {
actionFunc = () => {
// some logic
// call recalculate function, that always return initial value
this.responsesTags.doCalculating()
}
render() {
return (
<div>
<ChildComponent
ref={instance => { this.responsesTags = instance }}
/>
<button onClick={() => this.actionFunc()} />
</div>
)
}
}
What is missing to recalculate a function when called in the parent component?
In my opinion your code works correctly, I've fiddle with your example (a little different), maybe it will be useful to you: https://jsfiddle.net/tu7vxfym/ . If I calculate height of the ul from child and parent component it will calculate correctly.
class ChildComponent extends React.Component {
constructor(){
super();
this.doCalculating = this.doCalculating.bind(this);
this.addDiv = this.addDiv.bind(this);
this.state = {
list: [],
height:undefined
}
}
componentDidMount() {
this.doCalculating()
}
doCalculating (){
const defaultHeight = 50
const newHeight = this.refs.tagList.clientHeight;
this.setState(state=>{
return state.height = this.refs.tagList.clientHeight
})
console.log(newHeight)
}
addDiv(){
this.setState(function(state){
return state.list.push(this.refs.tagList.clientHeight)
})
}
render() {
return (
<div>
<ul ref={"tagList"}>
{this.state.list.map((e,i)=>{
return (<li key={i}>{e}</li>)
})}
</ul>
<h1>Calculated height: {this.state.height}</h1>
<button onClick={this.addDiv}>Add list</button>
<button onClick={this.doCalculating}>Child button</button>
</div>
)
}
}
class ParentComponent extends React.Component {
constructor(){
super();
this.actionFunc = this.actionFunc.bind(this)
}
actionFunc(){
this.responsesTags.doCalculating()
}
render() {
return (
<div>
<ChildComponent ref={instance => { this.responsesTags = instance }}/>
<button onClick={this.actionFunc}>Parent button</button>
</div>
)
}
}

when changing a single todo into a textfield instead of all of todos

I was trying to create a react component. If a user clicks on the edit button on a to-do item, it should replace the specific clicked todo area with a text field.
What do I expect to see
When a user clicks on one of the to-do items, that item becomes into a text field, which can then be edited.
What do I actually see
When a user clicks on one of the to-do items, all items become into text fields.
Here is my code:
PARENT
handleEditClick = (e,id,text) => {
this.setState({val: !this.state.val})
}
render() {
return (
<div style={{display: "flex"}}>
<div>
{this.props.todos.map(todo => (
<div key={todo.id}>
<EditButton todo={todo} val={this.state.val} text={this.state.text}
handleEditClick={(e, id, text) => this.handleEditClick(e, id, text)}/>
</div>
))}
</div>
</div>
)
}
}
CHILD(EDITBUTTON):
const EditButton = (props) => {
if(!props.val) {
return(
<div>
<List>
<ListItem
role={undefined}
dense
button
onClick={() => this.updateTodo(props.todo)}
><Checkbox
checked={props.todo.complete}
/>
<ListItemText primary={props.todo.text} />
<ListItemSecondaryAction>
<Button mini color="secondary" variant="fab"
disabled={props.todo.complete}
onClick={(e) => props.handleEditClick(e, props.todo.id, props.todo.text)}
>
<Icon>edit_icon</Icon>
</Button>
<IconButton onClick={() => this.removeTodo(props.todo)}>
<CloseIcon />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
</List>
</div>
)
}else {
return(
<TextField
onChange={this.handleChange}
onKeyDown={this.handleKeyDown}
label=""
margin="normal"
fullWidth
value={props.text}
color="secondary"
/>
)
}
}
This example is not complete, but it does show how to implement what you descript. The program with your code was when you update the status of edit, it is updating every single item's status.
https://codesandbox.io/s/x7rvqm6xz4
import React from "react";
import { Button, TextField } from "#material-ui/core";
class Todos extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: null
};
}
componentDidMount() {
this.setState({
todos: this.props.todos.map(todo => ({ name: todo, isEdit: false }))
});
}
handleEdit(name) {
const { todos } = this.state;
const updateTodos = todos.map(todo => {
if (todo.name === name) {
return { name: todo.name, isEdit: !todo.isEdit };
}
return todo;
});
this.setState({ todos: updateTodos });
}
_renderTodos() {
if (!this.state.todos) {
return null;
}
return this.state.todos.map(todo => {
return (
<div key={todo.name}>
{todo.isEdit ? <TextField /> : <span>{todo.name}</span>}
<Button onClick={() => this.handleEdit(todo.name)}>Click</Button>
</div>
);
});
}
render() {
if (!this.props.todos.length > 0) {
return null;
}
return <div>{this._renderTodos()}</div>;
}
}
export default Todos;
Basically, this component takes a list of todos. Put them into a state, and this state tracks whether to display them as todos or TextField.
I've done this before. See in CODESANDBOX.
There is TodoList class that can accept todos and saveEditedTodo as props. It has editedTodo state which dinamically change when "Edit" button is clicked. Of course, it contains id and text.
import React from "react";
export default class extends React.Component {
state = {
editedTodo: null
};
toggleEditTodo = (todo = null) => {
this.setState({ editedTodo: todo });
};
onChangeTodoText = text => {
this.setState(prevState => ({
editedTodo: Object.assign(prevState.editedTodo, { text })
}));
};
submitTodoForm = e => {
e.preventDefault();
this.props.saveEditedTodo(this.state.editedTodo);
this.setState({
editedTodo: null
});
};
render() {
const { editedTodo } = this.state;
const { todos } = this.props;
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo === editedTodo ? (
// Show text field
) : (
// Show todo
)}
</li>
))}
</ul>
);
}
}

Categories