I am trying to send a prop up to the top level.
At the top level I'm trying to console.log the event text
At the moment I do not want to use Redux techniques
Here is the lower level component
import React, { Component } from "react";
export default class Search extends Component {
state = {
text: ""
};
onChange = e => {
this.setState({ [e.target.name]: e.target.value });
this.props.searchUsers(this.state.text);
};
render() {
return (
<div className="ui center aligned fluid container">
<div className="ui inverted segment">
<div className="ui inverted input">
<input
type="text"
name="text"
placeholder="Type Something"
value={this.state.text}
onChange={this.onChange}
/>
</div>
</div>
</div>
);
}
}
Then I try to console.log the text through an arrow function searchUsers
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";
import Search from "./components/Search";
const App = () => {
searchUsers = text => {
console.log(text);
};
return (
<div className="App">
<Search searchUsers={this.searchUsers} />
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
export default App;
The Error I get
Please help me identify the problem.
In App component, you shouldn't use this and should use const
I think this should work
const App = () => {
const searchUsers = text => {
console.log(text);
};
return (
<div className="App">
<Search searchUsers={searchUsers} />
</div>
);
};
you can't use this inside stateless react component
Try this for index.js:
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";
import Search from "./components/Search";
const App = () => {
const searchUsers = text => {
console.log(text);
};
return (
<div className="App">
<Search searchUsers={searchUsers} />
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
export default App;
Edit:
when I console.log I only see the letter after I type the next one...So if I physically type Hello...I console.log Hell.......any way to fix that ?
This is because you are passing the state as argument after setting the state. But setState is asynchronous so by that time the state is not set. You can either pass e.target.value or using callback in setState to invoke this.props.searchUsers
For eg:
onChange = e => {
this.setState({ [e.target.name]: e.target.value });
this.props.searchUsers(e.target.value);
};
This should fix your issue.
Hope it helps!
Related
On one page I have two inputs and a button, after clicking on the button goes to the second page, how do I get data from the inputs on the second page?
navigate(path, { state: { input_value: value }}) ?
index.js
import './App.css';
function App() {
return (
<div className='App'>
<header className='App-header'>
<input type='text' placeholder='Name'/>
<input type='text' placeholder='Surname'/>
<button type='submit'>Send</button>
</header>
</div>
);
}
export default App;
getData.js
export const getData = () => {
return (
<div>
<h1>Name:</h1>
<h1>Surname:</h1>
</div>
)
};
You can have a state variable in the App component and then pass the state as a prop to GetData:
import './App.css';
import { useState, useRef } from "react";
function App() {
const nameInput = useRef(null);
const surNameInput = useRef(null);
const [fullName, setFullName] = useState({
name: "",
surName: ""
});
const sendData = () => {
// you can do some input validation here
setFullName({
name: nameInput.current.value,
surName: surNameInput.current.value,
});
}
return (
<div className='App'>
<header className='App-header'>
<input type='text' placeholder='Name'
ref={nameInput}
/>
<input type='text' placeholder='Surname'
ref={surNameInput}
/>
<button onClick={sendData}>Send</button>
</header>
<!-- some more markup here -->
</div>
);
}
export default App;
Here's how you pass your data to GetData component:
<GetData fullName={fullName} />
And then in your GetData component you get the passed props and display them:
export const GetData = (fullName) => {
return (
<div>
<h1>Name: {fullName.name}</h1>
<h1>Surname: {fullName.surName}</h1>
</div>
)
};
You can read more about hooks like useState and useRef here
So this might be Part 1 of an answer.
Taking the code you have, I've put the fields in form tag, then called handleSubmit from the built in onSubmit that is called when you click a button of type='submit' inside a form.
The values is taken from the event parameter (e) of the onSubmit (you can identify these by the 'name' attribute on the input tags) and then I am using useState hook to store the two values.
This would be where part one ends. You have the data, and you can see how it is passed to the GetDate component (the deconstructed props {name, surname} that are passed in.
From there, you should follow the documentation for your chosen router and, if you run into trouble, post the code you've tried and I can continue to help.
https://v5.reactrouter.com/web/guides/quick-start
import React, { useState } from 'react';
import { GetData } from './GetData';
export function App() {
const [theName, setTheName] = useState('');
const [theSurname, setTheSurname] = useState('');
const handleSubmit = (e) => {
setTheName(e.target.name.value);
setTheSurname(e.target.surname.value);
e.preventDefault();
}
return (
<div className='App'>
<header className='App-header'>
<form onSubmit={handleSubmit}>
<input type='text' placeholder='Name' name='name'/>
<input type='text' placeholder='Surname' name='surname'/>
<button type='submit'>Send</button>
</form>
</header>
<GetData name={theName} surname={theSurname} />
</div>
);
}
export default App;
Here is a component like your getData function.
I've added it to the App component, just so you can see the values being displayed, but for what you are looking for, you will need to read the documentation for react-router-dom
import React from "react";
export const GetData = ({name, surname}) => {
return (
<div>
<h1>Name:{name}</h1>
<h1>Surname:{surname}</h1>
</div>
)
};
I am creating To do list app in React. It reads the input field correctly from state, but, when I added file, which reads all the information from input and outputs it as a new div with the name of todo item, it shows an error - "TypeError: Cannot destructure property 'name' of 'props.todo' as it is undefined".
import React, {useState} from 'react';
import './App.css';
import {Container, Row, Col} from 'react-bootstrap';
import AddForm from './components/AddForm';
import TodoList from './components/TodoList';
function App() {
const [todos, addTodos] = useState([]);
const addTodoHandler = todo => addTodos([...todos, todo]);
return (
<Container fluid>
<Row>
<h1>Todo list</h1>
<Col>
<TodoList todos={todos} />
</Col>
<Col>
<AddForm addTodoHandler={addTodoHandler}/>
</Col>
</Row>
</Container>
);
}
export default App;
import React from 'react';
const TodoList = (props) => {
const {name} = props.todo;
return (
<div>
<div>{name} </div>
<button>Delete</button>
</div>
);
}
export default TodoList;
import React from "react";
import { Button } from "react-bootstrap";
class AddForm extends React.Component {
state = {
name: "",
};
add = (e) => {
e.preventDefault();
if (this.state.name === "") {
alert("Todo cannot be empty");
return;
}
this.props.addTodoHandler(this.state);
alert(this.state.name)
this.setState({ name: "" });
};
render(){
return (
<div>
<h2>Add Todo</h2>
<form onSubmit={this.add}>
<input
type="text"
name="todo"
value={this.state.name}
onChange={(e) => this.setState({ name: e.target.value })}
/>
<Button type="submit">Add</Button>
</form>
</div>
);
}
};
export default AddForm;
As pointed out in comments you need to fix the destructuring statement to use the correct prop (todos instead of todo).
But you have an additional problem in that your <TodoList> component is not set up to render a list. You're passing todos as an array prop to <TodoList> but in <TodoList> you're using it as a single object. You'll need to fix that as well. If you want a list to be rendered you'll need to iterate over it. Something like this:
import React from 'react';
const TodoList = (props) => {
const list = props.todos || [];
return list.map(item =>
<div>
<div>{item.name} </div>
<button>Delete</button>
</div>
);
}
export default TodoList;
Note that this also checks for props.todos to be undefined and if so assigns list to be an empty array so that the render has a value and does not crash.
https://codesandbox.io/s/react-mobx-change-value-in-several-components-f2tuu
How to change a variable from different places?
The variable is one, but it changes separately in different components and these changes made in one component are not reflected in the other.
App.js
import AppStore from "./AppStore";
import { observer } from "mobx-react";
import SomeComponent from "./SomeComponent";
const store = AppStore();
const App = observer(() => {
return (
<div className="App">
<div>
<i>This is text in App:</i> {store.text}
</div>
<div>
<button
onClick={() => {
store.changeText("This is text from App");
}}
>
Change text from App
</button>
</div>
<SomeComponent />
</div>
);
});
export default App;
SomeComponent.js
import React from "react";
import AppStore from "./AppStore";
import { observer } from "mobx-react";
const store = AppStore();
const SomeComponent = observer((props) => {
return (
<div>
<div>
<i>This is text in component:</i> {store.text}
</div>
<div>
<button
onClick={() => store.changeText("This is text from component")}
>
Change text from component
</button>
</div>
</div>
);
});
export default SomeComponent;
AppStore.js
import { action, makeObservable, observable } from "mobx";
export default function AppStore() {
return makeObservable(
{
text: "Initial text",
changeText(data) {
this.text = data + ": " + new Date().toLocaleTimeString();
}
},
{
text: observable,
changeText: action
}
);
}
I found sulution with React Context.
Sorry for not adding here right away.
Unfortunately, it is difficult to understand the Mobx documentation.
https://codesandbox.io/s/react-mobx-change-value-in-several-components-forked-using-react-context-e1mjh
First, we need to make context and create provider:
https://codesandbox.io/s/react-mobx-change-value-in-several-components-forked-using-react-context-e1mjh?file=/src/AppStoreProvider.js
import { useLocalStore } from "mobx-react";
import React from "react";
import AppStore from "./AppStore";
export const AppStoreContext = React.createContext();
export const AppStoreProvider = ({ children }) => {
const store = useLocalStore(AppStore);
return (
<AppStoreContext.Provider value={store}>
{children}
</AppStoreContext.Provider>
);
};
export const UseAppStore = () => React.useContext(AppStoreContext);
export default AppStoreProvider;
Where the AppStore.js is:
import { action, makeObservable, observable } from "mobx";
export default function AppStore() {
return makeObservable(
{
text: "Initial text",
changeText(data) {
this.text = data + ": " + new Date().toLocaleTimeString();
}
},
{
text: observable,
changeText: action
}
);
}
https://codesandbox.io/s/react-mobx-change-value-in-several-components-forked-using-react-context-e1mjh?file=/src/AppStore.js
Then, we need to wrap our main render in index.js with :
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import AppStoreProvider from "./AppStoreProvider";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
<AppStoreProvider>
<App />
</AppStoreProvider>,
rootElement
);
https://codesandbox.io/s/react-mobx-change-value-in-several-components-forked-using-react-context-e1mjh?file=/src/index.js
And then we can use context:
import "./styles.css";
import React from "react";
import { UseAppStore } from "./AppStoreProvider";
import { observer } from "mobx-react";
import SomeComponent from "./SomeComponent";
const App = observer(() => {
const store = UseAppStore();
return (
<div className="App">
<div>
<i>This is text in App:</i> {store?.text}
</div>
<div>
<button
onClick={() => {
store.changeText("This is text from App");
}}
>
Change text from App
</button>
</div>
<SomeComponent />
</div>
);
});
export default App;
https://codesandbox.io/s/react-mobx-change-value-in-several-components-forked-using-react-context-e1mjh?file=/src/App.js
Or like this:
import { useContext } from "react";
import { AppStoreContext } from "./AppStoreProvider";
import { observer } from "mobx-react";
const SomeComponent = observer((props) => {
const store = useContext(AppStoreContext);
return (
<div>
<div>
<i>This is text in component:</i> {store.text}
</div>
<div>
<button onClick={() => store.changeText("This is text from component")}>
Change text from component
</button>
</div>
</div>
);
});
export default SomeComponent;
https://codesandbox.io/s/react-mobx-change-value-in-several-components-forked-using-react-context-e1mjh?file=/src/SomeComponent.js
Hope it comes in handy for someone
Inside your App component, you created an object variable store via this code const store = AppStore();. Then you use it, display it, change it and so on. Later on inside your SomeComponent component you create a NEW variable store via const store = AppStore();. Even tho the variable names are the same and using the object, these are 2 DIFFERENT variables. Hence why updating one does not update the other. Inside the DOM they are a different object. To fix it instead of creating a new object inside the SomeComponent just pass store from the parent component. Inside the App, when rendering SomeComponent pass it the store variable like so
<SomeComponent store={store} />
And inside your SomeComponent take it from props and use it
import React from "react";
import { observer } from "mobx-react";
const SomeComponent = observer(({ store }) => {
return (
<div>
<div>
<i>This is text in component:</i> {store.text}
</div>
<div>
<button onClick={() => store.changeText("This is text from component")}>
Change text from component
</button>
</div>
</div>
);
});
export default SomeComponent;
This way both components can change the same variables data.
EDIT. I forked your project and edited the code to match what I posted here so you would have a live example to analize. https://codesandbox.io/s/react-mobx-change-value-in-several-components-forked-4c7uk?file=/src/SomeComponent.js
Focus is not working on the input when "isFocussed" is true
componentWillReceiveProps(nextProps){
if(nextProps.isFocussed){
this.myRef.current.focus();
}
}
<input ref={this.myRef} />
You are not using createRef and trying to use focus method which is the issue.
Simply create callback ref and you are good to go.
Here is the working code below and codesandbox link.
index.js
import React from "react";
import ReactDOM from "react-dom";
import FocusComp from "./FocusComp";
class App extends React.Component {
state = {
focus: false
};
render() {
return (
<div className="App">
<button onClick={() => this.setState({ focus: true })}>
click me to focus
</button>
<br />
<br />
<FocusComp isFocussed={this.state.focus} />
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
FocusComp.js
import React from "react";
class FocusComp extends React.Component {
componentWillReceiveProps(nextProps) {
if (nextProps.isFocussed) {
this.myRef.focus();
}
}
render() {
return <input ref={myRef => (this.myRef = myRef)} />;
}
}
export default FocusComp;
I created a working code to make you understand what you did wrong.
Things to improve:-
1) use createRef instead of callback.
2) Don't use componentWillReceiveProps as it's deprecated.
Hope that helps!!!
I have read lot of answers here but all of them are tide to class components.
If I have simple functional component where I use useRef or createRef, ref.current is undefined
I'm assigning it on top of div or input but I can't get any of their properties
Console.log() gives me data only when I use standelone console.log(ref)
Every other property is undefined e.g. console.log(ref.current)
import React, { useRef } from "react";
import ReactDOM from "react-dom";
function App() {
const ref = useRef()
console.log(ref.current) // undefined
console.log(ref) // { current }
return (
<div className="App">
<h1 ref={ref}>Hello CodeSandbox</h1>
{/* <input ref={ref} name="test" value="bla" /> */}
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
Take a look on this demo and look at console:
https://codesandbox.io/s/fervent-kirch-soe8n
But even in class component I can't access for example ref.current.innerHTML:
https://codesandbox.io/s/relaxed-beaver-ic1em
Ok I have found where is problem.
If I use useEffect() or if I use some button handler, I have access to element:
useEffect(()=>{
console.log(ref.current.innerHTML)
})
same in class component:
class App extends React.Component {
myRef = React.createRef();
componentDidMount(){
console.log(this.myRef.current); // correct
}
render() {
console.log(this.myRef.current); // null
return <h2 ref={this.myRef}>Start editing to see some magic happen!</h2>
}
}
or
<button onClick={()=> console.log(ref.current.innerHTML)}>Click</button>
You give the ref to the element in the dom,
so you can't access it before it actually gets render,
so in react that occur in the component lifecycle,
which is either useEffect or componentDidMount.
import React, { useRef, useEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const ref = useRef();
useEffect(() => {
console.log(ref.current)
},[])
return (
<div className="App">
<h1 ref={ref}>Hello CodeSandbox</h1>
{/* <input ref={ref} name="test" value="bla" /> */}
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
const Expander = forwardRef((_, ref) => {
return <div ref={ref}>test</div>;
});
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Way without ref and with ref and useState
import React, { useRef, useEffect, useState } from "react";
import ReactDOM from "react-dom";
function app() {
const ref = useRef('lalala');
const [h1Text, setH1Text] = useState('Hello CodeSandbox');
useEffect(() => {
console.log(ref.current)
});
const changeHeader = () =>{
setH1Text('mamamama');
}
const changeHeader2 = (ev) =>{
ev.target.innerHTML = "Hello CodeSandbox222222222222";
}
return (
<div className="App">
<h1 ref={ref} onClick={changeHeader}>{h1Text}</h1>
<h2 onClick={changeHeader2}>Hello CodeSandbox2</h2>
</div>
);
}
And code that input chagne the h1 without refs
function Example2() {
const [h1Text, setH1Text] = useState('Hello CodeSandbox');
const changeHeader = (ev) =>{
setH1Text(ev.target.value);
}
return (
<div className="App">
<h1>{h1Text}</h1>
<input onChange={changeHeader} name="test" />
</div>
);
}
If you referring component directly without mounting (componentDidMount/useEffect) then you will be get an undefined value. Alternatively you can use arrow function to access ref element.
example:
export function App(props) {
const ref = useRef()
console.log(ref.current) // undefined
const seeRef = () => {
console.log(ref.current) // <h1 ></h1>
}
return (
<div className='App'>
<h1 ref={ref}>Hello React.</h1>
<button onClick={()=> seeRef()}>Click</button>
</div>
);
}
Ref: https://stackoverflow.com/a/72548440/4652706