Function components cannot have string refs. We recommend using useRef() instead - reactjs

I'm creating a counter state using useState and useRef. However I'm getting this error
Here's my code
import { useEffect, useRef, useState} from 'react';
const App = () => {
const [clicks, setClick] = useState(0)
const myComponentDiv = useRef(null)
useEffect(() => {
if (myComponentDiv && myComponentDiv.current) {
myComponentDiv.current.addEventListener('click', clickHandler)
return () => {
myComponentDiv.current.removeEventListener('click', clickHandler)
}
}
}, [myComponentDiv]);
const clickHandler = () => {
setClick(clicks + 1)
}
return (
<div className="App">
<div className="my-component" ref="myComponentDiv">
<h2>My Component {clicks} clicks</h2>
</div>
</div>
);
}
export default App;
May i know where i did wrong?

Here:
ref="myComponentDiv"
should be:
ref={myComponentDiv}

Related

Run useReadCypher inside useEffect

I'm writing React functional component that should be input for search on Neo4j.
I'm dependant on the useReadCypher and cannot change it's inner implementation.
I cannot write the useReadCypher inside the useEffect because it's break the rule of hooks.
import React, { useState, useEffect, useCallback } from 'react';
import { useReadCypher } from "use-neo4j";
export default function Search() {
const [count, setCount\] = useState(0);
const [runQuery, setRunQuery\] = useState(false);
const query = `MATCH (n) RETURN n LIMIT ${count}`;
const data = useReadCypher(query);
const handleClick = useCallback(() => {
setCount(count + 1);
setRunQuery(true);
}, [count]);
useEffect(() => {
if (runQuery) {
console.log('Data changed', data);
setRunQuery(false);
}
}, [data, runQuery]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={handleClick}>
Click me
</button>
{JSON.stringify(data)}
</div>
);
}
I want to be able to click on the button to rerun the query using the useReadCypher.
What should be the approach to solving this issue?
Thank you.
It was the solution. Here is my final component.
import React, { useState, useEffect, useCallback } from 'react';
import { useReadCypher } from "use-neo4j";
import {Header} from "semantic-ui-react";
import {StyledDiv, StyledInput} from "./Style";
export default function Search() {
const [term, setTerm] = useState('');
const [runQuery, setRunQuery] = useState(false);
const query = `MATCH (n) RETURN n LIMIT ${term}`;
const {records, run} = useReadCypher(query);
const handleClick = useCallback(() => {
setRunQuery(true);
run();
}, [term]);
useEffect(() => {
if (runQuery) {
console.log('Data changed', records);
setRunQuery(false);
}
}, [records, runQuery]);
return (
<>
<Header as='H2' color='blue' textAlign='center' block>Search</Header>
<StyledDiv>
<StyledInput
value={term}
onChange={(e: any) => setTerm(e.target.value)}
/>
<button onClick={handleClick}>Search</button>
</StyledDiv>
<div>
{JSON.stringify(records)}
</div>
</>
);
}

map() is not rendering html elements

I had spent 4hrs in this issue but can't figure out what's the problem. Through props I had passed an array(props.content) and wanted to render each element with <h4> tag(not specific) but I can't. It works when I try to console.log each elements of array.
import classes from "./Memes.module.css";
const Memes = (props) => {
return (
<div className={classes.memes__body}>
{Array.prototype.forEach.call(props.content,items=>{
console.log(items)
})}
</div>
);
}
export default Memes;
Output -
but It didn't work when I run the same to render all items in some html tags.
import classes from "./Memes.module.css";
const Memes = (props) => {
return (
<div className={classes.memes__body}>
{Array.prototype.forEach.call(props.content,items=>{
<h1>{items}</h1>
})}
</div>
);
}
export default Memes;
OutPut
Doesn't work.
Note - Here props.content is an array of strings.
Fetch_memes.js (Parent one)
import { useState } from "react";
import { useEffect } from "react";
import Memes from "./Memes";
const Fetch_memes = () => {
const [image, setImage] = useState();
const [text, setText] = useState("");
useEffect(() => {
const memes = async () => {
const response = await fetch('https://www.reddit.com/r/memes.json');
if (!response.ok) {
throw new Error("Something went wrong");
}
const responseData = await response.json();
const img = responseData.data.children[0].data.thumbnail;
const memesCollection = [];
memesCollection.push("If you can Code then this doesn't mean that your are developer");
for(let i=0 ; i<20 ; i++){
memesCollection.push(responseData.data.children[i].data.title);
}
console.log(memesCollection[1]);
setImage(img);
setText(memesCollection);
}
memes();
}, []);
return (
<Memes src={image} content={text}/>
);
}
export default Fetch_memes;
You need to use return in your code:
import classes from "./Memes.module.css";
const Memes = (props) => {
return (
< >
{props.content.map((items)=>{
return <h1>{items}</h1>
})}
</>
);
}
export default Memes;
you can either use
{props.content.map(items=>{
return <h4>{items}</h4>
})}
or replace {} by ()and react returns the value by default:
{props.content.map(items=>(<h4>{items}</h4>))}
UPDATE: try this:
import classes from "./Memes.module.css";
import React from "react"
const Memes = ({content})=>{
return (
<div>
{content.map(item=>(<h1>{item}</h1>))}
</div>
);
}
export default Memes;
let me know the result.

React - Error: Rendered more hooks than during the previous render

why do I get the above error in React? - if I take out the useState for 'count2' in Person.js it works well - but surely this should work?
I've got two files: Persons.js that renders a list of Person.js. 'people' array is passed in
import React, { useEffect, useRef, useContext, useState } from 'react';
import Person from './Person/Person';
const Persons = (props) => {
const [count, setCount] = useState(0);
const [people, setPeople] = useState([]);
useEffect(() => {
setPeople(props.people);
},
[] // this to ensure a one-off!
);
let increaseMe = () => {
setCount(count + 1);
}
let clickA = (event ) => {
let newPeople = [...people];
newPeople.push({ name: 'Tom', age: 16 })
setPeople(newPeople);
}
let list = people.map(p => Person({name: p.name, 'age': p.age}));
return (
<React.Fragment>
<div>Hello Persons - { props.message } </div>
<button className='btnStyle' onClick={increaseMe}>IncreaseMe</button> { count }
<button onClick={clickA}>click A</button>
{ list }
</React.Fragment>
)
}
export default Persons;
import React, { useState } from 'react';
const Person = props => {
const [count2, setCount2] = useState(0);
return (
<div key={props.name}>
{ props.name } - { props.age} - { count2 }
</div>
)
}
export default Person;
The error comes from this line:
let list = people.map(p => Person({name: p.name, 'age': p.age}));
That's not how you render components in React, what you done is to call a function named Person, you want to call React.createElement instead (render a function component), which is just a JSX:
let list = people.map(p => <Person key={p.id} name={p.name} age={p.age} />);
Of course, if you want to call a function which returns JSX, you can't use hooks in it, because hooks are ONLY for function components (read about Rules Of Hooks).
try this :
let list = people.map(p => <Person key={p.name} name={p.name} age={p.age} );
And the key is not necessary on your Person component.

Using Draft js mention plugin with react hooks

I have been trying to get draft js mention plugin to work with react hooks but can't seem to figure what's wrong with the code. Appreciate any help on this.
import React, { useRef, useState, useEffect } from "react";
import { EditorState } from "draft-js";
import Editor from "draft-js-plugins-editor";
import createMentionPlugin, { defaultSuggestionsFilter } from "draft-js-mention-plugin";
import mentions from "./mentions";
export default function MentionEditor() {
const [editorState, setEditorState] = useState(EditorState.createEmpty());
const [suggestions, setSuggestions] = useState(mentions);
const editor = useRef(null);
useEffect(() => {
editor.current.focus();
}, [])
const mentionPlugin = createMentionPlugin();
const { MentionSuggestions } = mentionPlugin;
const plugins = [mentionPlugin];
const onSearchChange = ({ value }) => {
setSuggestions(defaultSuggestionsFilter(value, mentions))
};
return (
<div style={{ border: "1px solid gray" }}>
<Editor
editorState={editorState}
onChange={editorState => setEditorState(editorState)}
plugins={plugins}
ref={editor}
/>
<MentionSuggestions
onSearchChange={onSearchChange}
suggestions={suggestions}
/>
</div>
);
}
You need to move the draft-js plugin configuration outside the component arrow function. This is a pretty basic Draft-JS implementation using a functional component and hooks:
import React, { useState, useRef } from 'react'
import { EditorState } from 'draft-js'
import Editor from 'draft-js-plugins-editor'
import createMentionPlugin, { defaultSuggestionsFilter } from 'draft-js-mention-plugin'
import 'draft-js/dist/Draft.css'
import 'draft-js-mention-plugin/lib/plugin.css'
import mentions from "./mentions"
// Draft-JS-Mentions plugin configuration
const mentionPlugin = createMentionPlugin()
const { MentionSuggestions } = mentionPlugin
const plugins = [mentionPlugin]
const MyEditor= () => {
const [suggestions, setSuggestions] = useState(mentions)
// Draft-JS editor configuration
const [editorState, setEditorState] = useState(
() => EditorState.createEmpty(),
)
const editor = useRef(null)
// Check editor text for mentions
const onSearchChange = ({ value }) => {
setSuggestions(defaultSuggestionsFilter(value, mentions))
}
const onAddMention = () => {
}
// Focus on editor window
const focusEditor = () => {
editor.current.focus()
}
return (
<div onClick={() => focusEditor()}>
<Editor
ref={editor}
editorState={editorState}
plugins={plugins}
onChange={editorState => setEditorState(editorState)}
placeholder={'Type here...'}
/>
<MentionSuggestions
onSearchChange={onSearchChange}
suggestions={suggestions}
onAddMention={onAddMention}
/>
</div>
)
}
export default MyEditor
Just move these lines outside component and it will work:
const mentionPlugin = createMentionPlugin();
const { MentionSuggestions } = mentionPlugin;
const plugins = [mentionPlugin];
export default function MentionEditor() {
const [editorState, setEditorState] = useState(EditorState.createEmpty());
.. ... ...
}
!!!!!!!!!!!!!!!! PAY ATTENTION !!!!!!!!!!!!
The onSearchChange method will be triggered once the '#' character is typed, so in this case it will return just 5 items that fit the empty string...
To prevent this to be happened, just check that the value we want to search is not empty:
const onSearchChange = ({ value }) => {
if (value) {
setSuggestions(defaultSuggestionsFilter(value, mentions));
}
};

React: Access to the (updated) state with useState: it is not updated inside the component that creates it, but it is outside where it is called

Why am I not having access to the updated recipes (useState) value from inside the component that defines it?
In this example you can see how not being able to access to this value causes an error in the app once the reference to a function that I use to update the state is deleted
=> Codebox and code below
*Click two times the <h1> to see the error
https://codesandbox.io/s/sparkling-sea-5iqgo?fontsize=14&hidenavigation=1&theme=dark
import React, { useEffect, useState } from "react";
export default function App() {
const [userRecipes, setUserRecipes] = useRecipesData();
return (
<div className="App">
<h1
onClick={() => {
userRecipes.setBookmarks("onetwothree");
}}
>
Hello CodeSandbox
</h1>
<h2>{userRecipes.bookmarked_recipes}</h2>
</div>
);
}
const useRecipesData = () => {
const [recipes, setRecipes] = useState({});
const setBookmarks = newRecipes => {
console.log(recipes); // is undefined !? and deletes setBookmarks
setRecipes({
bookmarked_recipes: newRecipes,
setBookmarks: recipes.setBookmarks
});
};
useEffect(() => {
setRecipes({
bookmarked_recipes: "testtesttest",
setBookmarks: setBookmarks
});
}, []);
return [recipes, setRecipes];
};
What I don't understand is why if I return [recipes, setRecipes] where recipes.setBookmarks stores a reference to a function, it doesn't work
But if I return the function itself (which is a reference as well) [recipes, setBookmarks] then it works
See this other codebox where it does work
https://codesandbox.io/s/red-violet-gju99?fontsize=14&hidenavigation=1&theme=dark
import React, { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const [userRecipes, setUserRecipes] = useRecipesData();
return (
<div className="App">
<h1
onClick={() => {
setUserRecipes("onetwothree" + Math.random());
}}
>
Hello CodeSandbox
</h1>
<h2>{userRecipes.bookmarked_recipes}</h2>
</div>
);
}
const useRecipesData = () => {
const [recipes, setRecipes] = useState({});
const setBookmarks = newRecipes => {
console.log(recipes); // is defined this time
setRecipes({
bookmarked_recipes: newRecipes,
setBookmarks: recipes.setBookmarks
});
};
useEffect(() => {
setRecipes({
bookmarked_recipes: "testtesttest",
setBookmarks: setBookmarks
});
}, []);
return [recipes, setBookmarks];
};
It's all about context.
If you'll put console.log(receipes) in useEffect and the render function itself, you can see what the flow of events are:
First render recipe is empty.
UseEffect is called and puts setBookmark in recipe (but the recipe for setBookmark is empty)
Second render is called, and now recipe has "testesttest" and recipe.setBookmark is a function where the recipe object that is bound to it is the recipe value from event 1
setBookmark is called, recipe is now set to "onetwothree" but the recipe object is empty so we set the setBookmark to undefined.
instead of keeping the function inside the state, you need to just call it directly (I.E. return setBookmark and not setRecipes, like this:
import React, { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const [userRecipes, setBookmarks] = useRecipesData();
return (
<div className="App">
<h1
onClick={() => {
setBookmarks("onetwothree" + Math.random());
}}
>
Hello CodeSandbox
</h1>
<h2>{userRecipes.bookmarked_recipes}</h2>
</div>
);
}
const useRecipesData = () => {
const [recipes, setRecipes] = useState({});
const setBookmarks = newRecipes => {
setRecipes({
bookmarked_recipes: newRecipes,
});
};
useEffect(() => {
setRecipes({
bookmarked_recipes: "testtesttest",
});
}, []);
return [recipes, setBookmarks];
};

Resources