简体   繁体   中英

UseState with useContext not triggering re-renders

I am working on an application where I am passing useState as a value in the context provider. The code is as seen in the below code.

//App.js
import React,{useState,useContext} from 'react';
import logo from './logo.svg';
// import './App.css';
// import '../src/assets/tailwind.css'
import OneNote from '../src/components/onenote'
import OneNoteChild from '../src/components/OneNoteChild'
import HomePage from '../src/components/HomePage'
import { useFormik } from 'formik';
import {SearchProvider,SearchConsumer} from '../src/SearchCon';


const algoliasearch = require("algoliasearch");

const client = algoliasearch("asdasd", "code here");
const index = client.initIndex("articles");


async function Searh(term){
  var t = await index
  .search(term)
  .then(({ hits }) => {
    // console.log(hits);
    return hits
  })
  .catch(err => {
    console.log(err);
  });
  return t;
}
function App() {
  const [hit, setHit] = useState('')
  const [searchItems, setsearchItems] = useState([])
  const formik = useFormik({
    initialValues: {
      search: '',
    },
    onSubmit: values => {
      setsearchItems([])
      var temp = index
      .search(values.search,{hitsPerPage:100})
      .then(({ hits }) => {
        // console.log(hits);
        // return hits
        setHit(hits)
      })
      .catch(err => {
        console.log(err);
      });
      // var hits = Searh(values.search)
      // console.log(hits)
      // setHit(hits)
    },
  });
  
  
    
  
  return (
        <SearchProvider value={[searchItems, setsearchItems]}>
    <div className="App" class="bg-gray-200 font-light font-serif p-4">
      <form onSubmit={formik.handleSubmit} class='pr-8 pl-8 mt-8' class='sticky'>
        <input
          id="search"
          name="search"
          type="search"
          onChange={formik.handleChange}
          value={formik.values.search}
          class="bg-white focus:outline-none focus:shadow-outline border border-gray-300 rounded-lg py-2 px-4 block w-full appearance-none leading-normal" 
        />
      </form>
      
        <HomePage hit={hit}/>
        
    </div>
      </SearchProvider>
  );
}

export default App;

Now the home page is divided into 2 sections. The first section displays the result of the search as seen in the code below (OneNote.js is the file which displays the result). The code displayed in the OneNote section renders text. Each word in the text is clickable. This click results in addition of an element in the state that gets passed down through the context. As a result of addition on new element in the state a new element gets rendered. The render code is in the SearchCon.Consumer section in HomePage.js file below.

//HomePage.js
import React,{useContext} from 'react'
import SearchCon,{SearchConsumer} from '../SearchCon'
import OneNote from './onenote'
import OneNoteChild from './OneNoteChild'

function HomePage(props){
    const [searchItems, setsearchItems] = useContext(SearchCon);
    return ( 
        
    <div class='flex overflow-x-hidden text-base'>
          <div class='flex text-base grid-flow-row w-1/3 border-r-2 border-gray-500'>
            <div class="divide-y divide-gray-400 pr-1 pl-1 max-w-lg">
              {props.hit===''?<></>:props.hit.map((item)=>
              <OneNote key={item.objectID} ol={item}/>
              )}
            </div>
          </div>
          <SearchCon.Consumer>
            {context=>
          <div class='w-2/3 flex overflow-auto text-gray-600'>
            
            {
              searchItems.length>0?searchItems.map((item)=><OneNoteChild word={item}/>):<></>
            }
          </div>}
          </SearchCon.Consumer>
        </div>
        )
}

export default HomePage;

The file below is OneNote.js which is used in HomePage to display the search results.

//OneNote.js
import React,{useContext} from 'react'
import SearchCon from '../SearchCon'
const algoliasearch = require("algoliasearch");

const client = algoliasearch("asdf", "adfad");
const index = client.initIndex("articles");

function OneNote(props){
  const [searchItems, setsearchItems] = useContext(SearchCon);
    console.log(props)
    var all = props.ol.text.split(' ')

    function GD(wrd){
        if(searchItems.length<1){
          setsearchItems([wrd])
        }else{
        var temp = searchItems
        console.log(searchItems)
        console.log(typeof(temp))
        temp.push(wrd)
        setsearchItems(temp)
        }
    }

    

    return <div class=' p-6' key={props.ol.objectID}>
        {all.map((word)=><><button class='bg-gray-200 border-gray-200' onClick={()=>GD(word)}>{word}</button>{" "}</>)}
        </div>
}

export default (OneNote);

Now the problem I am facing is that the context is not getting updated immediately when the text rendered by the OneNoteChild is clicked(the file is below). Only after I type something into the input in the App.js file it triggers the change in context and results into the re-rendering.

//OneNoteChild.js
import React,{useContext,useState} from 'react'
import SearchCon from '../SearchCon'
import SearchResItem from '../components/SearchResItem'
const algoliasearch = require("algoliasearch");

const client = algoliasearch("asdfadsfa", "adsfadf");
const index = client.initIndex("articles");

function OneNoteChild(props){
  const [searchItems, setsearchItems] = useContext(SearchCon);
  const [res, setres] = useState("")

  if(res===''){
    var temp = index
    .search(props.word,{hitsPerPage:100})
    .then(({ hits }) => {
      setres(hits)
    })
    .catch(err => {
      console.log(err);
    });
  }

  if(res===''){
      return <div>Loading</div>
  }
  function Wrd(){
    var temp = [];
    for (let index = 0; index < searchItems.length; index++) {
      const element = searchItems[index];
      if(element===props.word){
        
      }else{
        temp.push(element)
      }
    }
    setsearchItems(temp)
  }
  
    return <div class='p-6 divide-y divide-gray-400 w-78 border-black shadow-xl m-2' >
        {props.word}<button className='float-right' onClick={Wrd}>x</button><br/>
        {res.length>0?res.map((item)=><SearchResItem ol={item} word={props.word}/>):<div>No Results</div>}
        </div>
}

export default (OneNoteChild);
//SearchResItem.js
import React,{useContext} from 'react'
import SearchCon from '../SearchCon'

function SearchResItem(props){
    const [searchItems, setsearchItems] = useContext(SearchCon);
    var all = props.ol.text.split(' ')
    function GD(wrd){
        //Add this word
        console.log('Add')
        if(searchItems.length<1){
            setsearchItems([wrd])
          }else{
          var temp = searchItems
          console.log(temp)
          console.log(typeof(temp))
          temp.push(wrd)
          setsearchItems(temp)
          }
    }
    return <div class='p-2 w-70'>
        {all.map((word)=><><button class='bg-gray-200 border-gray-200' onClick={()=>GD(word)}>{word===props.word?<div class='text-blue-600'>{word}</div>:word}</button>{" "}</>)}
        <br/><a href={props.ol.url} target='_blank' class='text-red-400'>Link</a>
        </div>
}

export default (SearchResItem);
//SearchCon.js
import React from 'react'

const SearchCon = React.createContext()

export const SearchProvider = SearchCon.Provider
export const SearchConsumer = SearchCon.Consumer

export default SearchCon

I am not sure what am I doing wrong here.

Any help would be useful.

If you use 'useContext', you dont need the 'consumer'. Further, you don't use the arg 'context' of 'consumer'. So, try without the 'consumer'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM